-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: MCP authorization parameter implementation #4052
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix: MCP authorization parameter implementation #4052
Conversation
|
Can you point me to where in the Responses API spec it has this |
|
@bbrowning Thanks for your comment! Yes, I changed it to 'authorization' However, this static approach would only be helpful for MCP credentials that are hardcoded in tool definitions (long lived tokens). But its not ideal for cases where we need to have different mcp credentials per user. Automatically forwarding the user's OAuth token to MCP server is not an option, so an alternative approach would be for the user to explicitly pass their own OAuth token through the client? (dynamic per-request) |
I'm not sure I follow what you're saying. Every inference request passes in the tools available for that request. So, with every inference request, the client can pass in an updated token for any MCP servers that request references. And that means every user also passes in their own credentials. Or, am I misunderstanding how you intend this to work? |
This PR supports the case where authorization tokens change between response creation requests. For example: response1 = client.responses.create( response2 = client.responses.create( within a single response, multiple inference iterations happen --> authorization tokens can not be updated between these inference iterations. Internally, this might do:
|
|
this approach is static within each individual response but dynamic across responses. |
|
|
||
|
|
||
| @json_schema_type | ||
| class MCPAuthorization(BaseModel): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should adopt exactly the type specified here: https://platform.openai.com/docs/api-reference/responses/create#responses_create-tools-mcp_tool-authorization otherwise we are forking off our Responses API without a good reason. (This is beyond the fact that passing { username, password } being not correct all.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ashwinb thanks for your comment, I was about to handle this point. Removed that type
mattf
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove all the reformatting and make it clear what is being changed.
|
This pull request has merge conflicts that must be resolved before it can be merged. @omaryashraf5 please rebase it. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be simply -
- add
authzfield toOpenAIResponseInputToolMCP - pass
authzto mcp client - sanitize response to not include
authzfield
we should not be providing multiple ways to provide the token.
we must not be passing a token for one service, e.g. inference, to another, e.g. mcp server.
- Add Field(exclude=True) to authorization parameter to prevent token leakage in responses - Add model validator to reject Authorization header in headers dict - Users must use dedicated 'authorization' parameter instead of headers - Headers field is preserved for legitimate non-auth headers (tracing, routing, etc.) This implements the security requirement that authorization params are never returned in responses, unlike generic headers which may be echoed back.
Updated test_mcp_authorization_error_when_header_provided to match the new validation error message from the Pydantic validator.
Per reviewer feedback, API models should be pure data structures without business logic. Moved the Authorization header validation from the Pydantic @model_validator in openai_responses.py to the handler in streaming.py. - Removed @model_validator from OpenAIResponseInputToolMCP - Added validation at handler level in _process_mcp_tool() - Maintains same security check: rejects Authorization in headers dict - Follows separation of concerns: models are data, handlers have logic
|
This pull request has merge conflicts that must be resolved before it can be merged. @omaryashraf5 please rebase it. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork |
Per reviewer feedback, validation should be in the openai_responses.py handler, not the streaming.py file. Moved validation logic to create_openai_response() method which is the main entry point for response creation. - Added validation in create_openai_response() before processing - Removed duplicate validation from _process_mcp_tool() in streaming.py - Validation runs early and rejects malformed requests immediately - Maintains same security check: rejects Authorization in headers dict
Addresses reviewer concern about token isolation between services. The remote provider now rejects Authorization headers in mcp_headers to prevent accidentally passing inference tokens to MCP servers. This makes the remote provider consistent with the inline provider: - Both reject Authorization in headers dict - Both require dedicated authorization parameter - Prevents token leakage across service boundaries Related changes: - Added validation in get_headers_from_request() - Throws ValueError if Authorization found in mcp_headers - Added TODO for dedicated authorization field in provider_data
Completes the TODO for extracting authorization from a dedicated field.
What changed:
- Added mcp_authorization field to MCPProviderDataValidator
- Updated get_headers_from_request() to extract from mcp_authorization
- Authorization is now properly isolated per MCP endpoint
API usage example:
{
"provider_data": {
"mcp_headers": {
"http://mcp-server.com": {
"X-Trace-ID": "trace-123"
}
},
"mcp_authorization": {
"http://mcp-server.com": "mcp_token_xyz789"
}
}
}
Security guarantees:
- Authorization cannot be in mcp_headers (validation rejects it)
- Each MCP endpoint gets its own dedicated token
- No cross-service token leakage possible
Thank you for your feedback.
Thanks for pointing out that we should have "one way to provide the token". I want to make sure I understand correctly: When you say we shouldn't provide multiple ways to provide the token, do you mean: Option A: Remove headers field entirely Option B: Keep headers but strictly enforce no Authorization in headers I went with option B but let me know if you disagree. As for your last point: I updated the |
Adds inline documentation to help users understand: - How to structure provider_data in HTTP requests - Where to place mcp_headers vs mcp_authorization - Security requirements (no Authorization in headers) - Token format requirements (without Bearer prefix) - Example usage with multiple MCP endpoints
Based on user feedback, improved comments to distinguish between the two security layers: 1. PRIMARY: Line 89 - Architectural prevention - get_request_provider_data() only reads from request body - Never accesses HTTP Authorization header - This is what actually prevents inference token leakage 2. SECONDARY: Lines 97-104 - Validation prevention - Rejects Authorization in mcp_headers dict - Enforces using dedicated mcp_authorization field - Prevents users from misusing the API Previous comment was misleading by suggesting the validation prevented inference token leakage, when the architecture already ensures that isolation.
✱ Stainless preview buildsThis PR will update the Edit this comment to update it. It will appear in the SDK's changelogs. ✅ llama-stack-client-go studio · code · diff
⏳ These are partial results; builds are still running. This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push. |
Updates integration tests to use the new mcp_authorization field
instead of the old method of passing Authorization in mcp_headers.
Changes:
- tests/integration/tool_runtime/test_mcp.py
- tests/integration/inference/test_tools_with_schemas.py
- tests/integration/tool_runtime/test_mcp_json_schema.py (6 occurrences)
All tests now use:
provider_data = {"mcp_authorization": {uri: AUTH_TOKEN}}
Instead of the old rejected format:
provider_data = {"mcp_headers": {uri: {"Authorization": f"Bearer {AUTH_TOKEN}"}}}
This aligns with the security architecture that prevents
accidentally leaking inference tokens to MCP servers.
Fixed incorrect import in test_mcp_authentication.py: - Changed: from llama_stack import LlamaStackAsLibraryClient - To: from llama_stack.core.library_client import LlamaStackAsLibraryClient This aligns with the correct import pattern used in other test files.
Added Field(exclude=True) to mcp_authorization field to ensure tokens are NEVER exposed in: - API responses (model_dump()) - JSON serialization (model_dump_json()) - Logs - Any Pydantic serialization This prevents accidental token leakage through: - Error messages - Debug logs - API response payloads - Monitoring/telemetry systems The field is still accessible within the application code but will be automatically excluded from all Pydantic serialization operations.
how are tracing, routing, etc related to this change? |
The current change just accepts the authorization field outside the header. I was just stating general pros for keeping the header field (apologies for the confusion) |
What does this PR do?
Adding a user-facing
authorizationparameter to MCP tool definitions that allows users to explicitly configure credentials per MCP server, addressing GitHub Issue #4034 in a secure manner.Test Plan
tests/integration/responses/test_mcp_authentication.py