fix: MCP authorization parameter implementation (#4052)

# What does this PR do?
Adding a user-facing `authorization ` parameter 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

---------

Co-authored-by: Omar Abdelwahab <omara@fb.com>
Co-authored-by: Ashwin Bharambe <ashwin.bharambe@gmail.com>
This commit is contained in:
Omar Abdelwahab 2025-11-14 08:54:42 -08:00 committed by GitHub
parent dc49ad3f89
commit eb545034ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 5205 additions and 62 deletions

View file

@ -2054,6 +2054,13 @@ paths:
required: false required: false
schema: schema:
$ref: '#/components/schemas/URL' $ref: '#/components/schemas/URL'
- name: authorization
in: query
description: >-
(Optional) OAuth access token for authenticating with the MCP server.
required: false
schema:
type: string
deprecated: false deprecated: false
/v1/toolgroups: /v1/toolgroups:
get: get:
@ -7123,6 +7130,10 @@ components:
- type: object - type: object
description: >- description: >-
(Optional) HTTP headers to include when connecting to the server (Optional) HTTP headers to include when connecting to the server
authorization:
type: string
description: >-
(Optional) OAuth access token for authenticating with the MCP server
require_approval: require_approval:
oneOf: oneOf:
- type: string - type: string
@ -9307,6 +9318,10 @@ components:
- type: object - type: object
description: >- description: >-
A dictionary of arguments to pass to the tool. A dictionary of arguments to pass to the tool.
authorization:
type: string
description: >-
(Optional) OAuth access token for authenticating with the MCP server.
additionalProperties: false additionalProperties: false
required: required:
- tool_name - tool_name

View file

@ -1878,6 +1878,13 @@ paths:
required: false required: false
schema: schema:
$ref: '#/components/schemas/URL' $ref: '#/components/schemas/URL'
- name: authorization
in: query
description: >-
(Optional) OAuth access token for authenticating with the MCP server.
required: false
schema:
type: string
deprecated: false deprecated: false
/v1/toolgroups: /v1/toolgroups:
get: get:
@ -6182,6 +6189,10 @@ components:
- type: object - type: object
description: >- description: >-
(Optional) HTTP headers to include when connecting to the server (Optional) HTTP headers to include when connecting to the server
authorization:
type: string
description: >-
(Optional) OAuth access token for authenticating with the MCP server
require_approval: require_approval:
oneOf: oneOf:
- type: string - type: string
@ -8366,6 +8377,10 @@ components:
- type: object - type: object
description: >- description: >-
A dictionary of arguments to pass to the tool. A dictionary of arguments to pass to the tool.
authorization:
type: string
description: >-
(Optional) OAuth access token for authenticating with the MCP server.
additionalProperties: false additionalProperties: false
required: required:
- tool_name - tool_name

View file

@ -2054,6 +2054,13 @@ paths:
required: false required: false
schema: schema:
$ref: '#/components/schemas/URL' $ref: '#/components/schemas/URL'
- name: authorization
in: query
description: >-
(Optional) OAuth access token for authenticating with the MCP server.
required: false
schema:
type: string
deprecated: false deprecated: false
/v1/toolgroups: /v1/toolgroups:
get: get:
@ -7123,6 +7130,10 @@ components:
- type: object - type: object
description: >- description: >-
(Optional) HTTP headers to include when connecting to the server (Optional) HTTP headers to include when connecting to the server
authorization:
type: string
description: >-
(Optional) OAuth access token for authenticating with the MCP server
require_approval: require_approval:
oneOf: oneOf:
- type: string - type: string
@ -9307,6 +9318,10 @@ components:
- type: object - type: object
description: >- description: >-
A dictionary of arguments to pass to the tool. A dictionary of arguments to pass to the tool.
authorization:
type: string
description: >-
(Optional) OAuth access token for authenticating with the MCP server.
additionalProperties: false additionalProperties: false
required: required:
- tool_name - tool_name

View file

@ -34,16 +34,16 @@ class ToolRuntimeRouter(ToolRuntime):
logger.debug("ToolRuntimeRouter.shutdown") logger.debug("ToolRuntimeRouter.shutdown")
pass pass
async def invoke_tool(self, tool_name: str, kwargs: dict[str, Any]) -> Any: async def invoke_tool(self, tool_name: str, kwargs: dict[str, Any], authorization: str | None = None) -> Any:
logger.debug(f"ToolRuntimeRouter.invoke_tool: {tool_name}") logger.debug(f"ToolRuntimeRouter.invoke_tool: {tool_name}")
provider = await self.routing_table.get_provider_impl(tool_name) provider = await self.routing_table.get_provider_impl(tool_name)
return await provider.invoke_tool( return await provider.invoke_tool(
tool_name=tool_name, tool_name=tool_name,
kwargs=kwargs, kwargs=kwargs,
authorization=authorization,
) )
async def list_runtime_tools( async def list_runtime_tools(
self, tool_group_id: str | None = None, mcp_endpoint: URL | None = None self, tool_group_id: str | None = None, mcp_endpoint: URL | None = None, authorization: str | None = None
) -> ListToolDefsResponse: ) -> ListToolDefsResponse:
logger.debug(f"ToolRuntimeRouter.list_runtime_tools: {tool_group_id}") return await self.routing_table.list_tools(tool_group_id, authorization=authorization)
return await self.routing_table.list_tools(tool_group_id)

View file

@ -49,7 +49,9 @@ class ToolGroupsRoutingTable(CommonRoutingTableImpl, ToolGroups):
routing_key = self.tool_to_toolgroup[routing_key] routing_key = self.tool_to_toolgroup[routing_key]
return await super().get_provider_impl(routing_key, provider_id) return await super().get_provider_impl(routing_key, provider_id)
async def list_tools(self, toolgroup_id: str | None = None) -> ListToolDefsResponse: async def list_tools(
self, toolgroup_id: str | None = None, authorization: str | None = None
) -> ListToolDefsResponse:
if toolgroup_id: if toolgroup_id:
if group_id := parse_toolgroup_from_toolgroup_name_pair(toolgroup_id): if group_id := parse_toolgroup_from_toolgroup_name_pair(toolgroup_id):
toolgroup_id = group_id toolgroup_id = group_id
@ -61,7 +63,7 @@ class ToolGroupsRoutingTable(CommonRoutingTableImpl, ToolGroups):
for toolgroup in toolgroups: for toolgroup in toolgroups:
if toolgroup.identifier not in self.toolgroups_to_tools: if toolgroup.identifier not in self.toolgroups_to_tools:
try: try:
await self._index_tools(toolgroup) await self._index_tools(toolgroup, authorization=authorization)
except AuthenticationRequiredError: except AuthenticationRequiredError:
# Send authentication errors back to the client so it knows # Send authentication errors back to the client so it knows
# that it needs to supply credentials for remote MCP servers. # that it needs to supply credentials for remote MCP servers.
@ -76,9 +78,11 @@ class ToolGroupsRoutingTable(CommonRoutingTableImpl, ToolGroups):
return ListToolDefsResponse(data=all_tools) return ListToolDefsResponse(data=all_tools)
async def _index_tools(self, toolgroup: ToolGroup): async def _index_tools(self, toolgroup: ToolGroup, authorization: str | None = None):
provider_impl = await super().get_provider_impl(toolgroup.identifier, toolgroup.provider_id) provider_impl = await super().get_provider_impl(toolgroup.identifier, toolgroup.provider_id)
tooldefs_response = await provider_impl.list_runtime_tools(toolgroup.identifier, toolgroup.mcp_endpoint) tooldefs_response = await provider_impl.list_runtime_tools(
toolgroup.identifier, toolgroup.mcp_endpoint, authorization=authorization
)
tooldefs = tooldefs_response.data tooldefs = tooldefs_response.data
for t in tooldefs: for t in tooldefs:

View file

@ -257,6 +257,19 @@ class OpenAIResponsesImpl:
stream = bool(stream) stream = bool(stream)
text = OpenAIResponseText(format=OpenAIResponseTextFormat(type="text")) if text is None else text text = OpenAIResponseText(format=OpenAIResponseTextFormat(type="text")) if text is None else text
# Validate MCP tools: ensure Authorization header is not passed via headers dict
if tools:
from llama_stack_api.openai_responses import OpenAIResponseInputToolMCP
for tool in tools:
if isinstance(tool, OpenAIResponseInputToolMCP) and tool.headers:
for key in tool.headers.keys():
if key.lower() == "authorization":
raise ValueError(
"Authorization header cannot be passed via 'headers'. "
"Please use the 'authorization' parameter instead."
)
guardrail_ids = extract_guardrail_ids(guardrails) if guardrails else [] guardrail_ids = extract_guardrail_ids(guardrails) if guardrails else []
if conversation is not None: if conversation is not None:

View file

@ -1091,10 +1091,12 @@ class StreamingResponseOrchestrator:
"server_url": mcp_tool.server_url, "server_url": mcp_tool.server_url,
"mcp_list_tools_id": list_id, "mcp_list_tools_id": list_id,
} }
# List MCP tools with authorization from tool config
async with tracing.span("list_mcp_tools", attributes): async with tracing.span("list_mcp_tools", attributes):
tool_defs = await list_mcp_tools( tool_defs = await list_mcp_tools(
endpoint=mcp_tool.server_url, endpoint=mcp_tool.server_url,
headers=mcp_tool.headers or {}, headers=mcp_tool.headers,
authorization=mcp_tool.authorization,
) )
# Create the MCP list tools message # Create the MCP list tools message

View file

@ -296,12 +296,14 @@ class ToolExecutor:
"server_url": mcp_tool.server_url, "server_url": mcp_tool.server_url,
"tool_name": function_name, "tool_name": function_name,
} }
# Invoke MCP tool with authorization from tool config
async with tracing.span("invoke_mcp_tool", attributes): async with tracing.span("invoke_mcp_tool", attributes):
result = await invoke_mcp_tool( result = await invoke_mcp_tool(
endpoint=mcp_tool.server_url, endpoint=mcp_tool.server_url,
headers=mcp_tool.headers or {},
tool_name=function_name, tool_name=function_name,
kwargs=tool_kwargs, kwargs=tool_kwargs,
headers=mcp_tool.headers,
authorization=mcp_tool.authorization,
) )
elif function_name == "knowledge_search": elif function_name == "knowledge_search":
response_file_search_tool = ( response_file_search_tool = (

View file

@ -276,7 +276,10 @@ class MemoryToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime):
) )
async def list_runtime_tools( async def list_runtime_tools(
self, tool_group_id: str | None = None, mcp_endpoint: URL | None = None self,
tool_group_id: str | None = None,
mcp_endpoint: URL | None = None,
authorization: str | None = None,
) -> ListToolDefsResponse: ) -> ListToolDefsResponse:
# Parameters are not listed since these methods are not yet invoked automatically # Parameters are not listed since these methods are not yet invoked automatically
# by the LLM. The method is only implemented so things like /tools can list without # by the LLM. The method is only implemented so things like /tools can list without
@ -304,7 +307,9 @@ class MemoryToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime):
] ]
) )
async def invoke_tool(self, tool_name: str, kwargs: dict[str, Any]) -> ToolInvocationResult: async def invoke_tool(
self, tool_name: str, kwargs: dict[str, Any], authorization: str | None = None
) -> ToolInvocationResult:
vector_store_ids = kwargs.get("vector_store_ids", []) vector_store_ids = kwargs.get("vector_store_ids", [])
query_config = kwargs.get("query_config") query_config = kwargs.get("query_config")
if query_config: if query_config:

View file

@ -49,7 +49,10 @@ class BingSearchToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime, NeedsReq
return provider_data.bing_search_api_key return provider_data.bing_search_api_key
async def list_runtime_tools( async def list_runtime_tools(
self, tool_group_id: str | None = None, mcp_endpoint: URL | None = None self,
tool_group_id: str | None = None,
mcp_endpoint: URL | None = None,
authorization: str | None = None,
) -> ListToolDefsResponse: ) -> ListToolDefsResponse:
return ListToolDefsResponse( return ListToolDefsResponse(
data=[ data=[
@ -70,7 +73,9 @@ class BingSearchToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime, NeedsReq
] ]
) )
async def invoke_tool(self, tool_name: str, kwargs: dict[str, Any]) -> ToolInvocationResult: async def invoke_tool(
self, tool_name: str, kwargs: dict[str, Any], authorization: str | None = None
) -> ToolInvocationResult:
api_key = self._get_api_key() api_key = self._get_api_key()
headers = { headers = {
"Ocp-Apim-Subscription-Key": api_key, "Ocp-Apim-Subscription-Key": api_key,

View file

@ -48,7 +48,10 @@ class BraveSearchToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime, NeedsRe
return provider_data.brave_search_api_key return provider_data.brave_search_api_key
async def list_runtime_tools( async def list_runtime_tools(
self, tool_group_id: str | None = None, mcp_endpoint: URL | None = None self,
tool_group_id: str | None = None,
mcp_endpoint: URL | None = None,
authorization: str | None = None,
) -> ListToolDefsResponse: ) -> ListToolDefsResponse:
return ListToolDefsResponse( return ListToolDefsResponse(
data=[ data=[
@ -70,7 +73,9 @@ class BraveSearchToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime, NeedsRe
] ]
) )
async def invoke_tool(self, tool_name: str, kwargs: dict[str, Any]) -> ToolInvocationResult: async def invoke_tool(
self, tool_name: str, kwargs: dict[str, Any], authorization: str | None = None
) -> ToolInvocationResult:
api_key = self._get_api_key() api_key = self._get_api_key()
url = "https://api.search.brave.com/res/v1/web/search" url = "https://api.search.brave.com/res/v1/web/search"
headers = { headers = {

View file

@ -10,8 +10,14 @@ from pydantic import BaseModel
class MCPProviderDataValidator(BaseModel): class MCPProviderDataValidator(BaseModel):
# mcp_endpoint => dict of headers to send """
mcp_headers: dict[str, dict[str, str]] | None = None Validator for MCP provider-specific data passed via request headers.
Phase 1: Support old header-based authentication for backward compatibility.
In Phase 2, this will be deprecated in favor of the authorization parameter.
"""
mcp_headers: dict[str, dict[str, str]] | None = None # Map of URI -> headers dict
class MCPProviderConfig(BaseModel): class MCPProviderConfig(BaseModel):

View file

@ -39,15 +39,29 @@ class ModelContextProtocolToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime
return return
async def list_runtime_tools( async def list_runtime_tools(
self, tool_group_id: str | None = None, mcp_endpoint: URL | None = None self,
tool_group_id: str | None = None,
mcp_endpoint: URL | None = None,
authorization: str | None = None,
) -> ListToolDefsResponse: ) -> ListToolDefsResponse:
# this endpoint should be retrieved by getting the tool group right? # this endpoint should be retrieved by getting the tool group right?
if mcp_endpoint is None: if mcp_endpoint is None:
raise ValueError("mcp_endpoint is required") raise ValueError("mcp_endpoint is required")
headers = await self.get_headers_from_request(mcp_endpoint.uri)
return await list_mcp_tools(mcp_endpoint.uri, headers)
async def invoke_tool(self, tool_name: str, kwargs: dict[str, Any]) -> ToolInvocationResult: # Phase 1: Support both old header-based auth AND new authorization parameter
# Get headers and auth from provider data (old approach)
provider_headers, provider_auth = await self.get_headers_from_request(mcp_endpoint.uri)
# New authorization parameter takes precedence over provider data
final_authorization = authorization or provider_auth
return await list_mcp_tools(
endpoint=mcp_endpoint.uri, headers=provider_headers, authorization=final_authorization
)
async def invoke_tool(
self, tool_name: str, kwargs: dict[str, Any], authorization: str | None = None
) -> ToolInvocationResult:
tool = await self.tool_store.get_tool(tool_name) tool = await self.tool_store.get_tool(tool_name)
if tool.metadata is None or tool.metadata.get("endpoint") is None: if tool.metadata is None or tool.metadata.get("endpoint") is None:
raise ValueError(f"Tool {tool_name} does not have metadata") raise ValueError(f"Tool {tool_name} does not have metadata")
@ -55,19 +69,57 @@ class ModelContextProtocolToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime
if urlparse(endpoint).scheme not in ("http", "https"): if urlparse(endpoint).scheme not in ("http", "https"):
raise ValueError(f"Endpoint {endpoint} is not a valid HTTP(S) URL") raise ValueError(f"Endpoint {endpoint} is not a valid HTTP(S) URL")
headers = await self.get_headers_from_request(endpoint) # Phase 1: Support both old header-based auth AND new authorization parameter
return await invoke_mcp_tool(endpoint, headers, tool_name, kwargs) # Get headers and auth from provider data (old approach)
provider_headers, provider_auth = await self.get_headers_from_request(endpoint)
# New authorization parameter takes precedence over provider data
final_authorization = authorization or provider_auth
return await invoke_mcp_tool(
endpoint=endpoint,
tool_name=tool_name,
kwargs=kwargs,
headers=provider_headers,
authorization=final_authorization,
)
async def get_headers_from_request(self, mcp_endpoint_uri: str) -> tuple[dict[str, str], str | None]:
"""
Extract headers and authorization from request provider data (Phase 1 backward compatibility).
Phase 1: Temporarily allows Authorization to be passed via mcp_headers for backward compatibility.
Phase 2: Will enforce that Authorization should use the dedicated authorization parameter instead.
Returns:
Tuple of (headers_dict, authorization_token)
- headers_dict: All headers except Authorization
- authorization_token: Token from Authorization header (with "Bearer " prefix removed), or None
"""
async def get_headers_from_request(self, mcp_endpoint_uri: str) -> dict[str, str]:
def canonicalize_uri(uri: str) -> str: def canonicalize_uri(uri: str) -> str:
return f"{urlparse(uri).netloc or ''}/{urlparse(uri).path or ''}" return f"{urlparse(uri).netloc or ''}/{urlparse(uri).path or ''}"
headers = {} headers = {}
authorization = None
provider_data = self.get_request_provider_data() provider_data = self.get_request_provider_data()
if provider_data and provider_data.mcp_headers: if provider_data and hasattr(provider_data, "mcp_headers") and provider_data.mcp_headers:
for uri, values in provider_data.mcp_headers.items(): for uri, values in provider_data.mcp_headers.items():
if canonicalize_uri(uri) != canonicalize_uri(mcp_endpoint_uri): if canonicalize_uri(uri) != canonicalize_uri(mcp_endpoint_uri):
continue continue
headers.update(values)
return headers # Phase 1: Extract Authorization from mcp_headers for backward compatibility
# (Phase 2 will reject this and require the dedicated authorization parameter)
for key in values.keys():
if key.lower() == "authorization":
# Extract authorization token and strip "Bearer " prefix if present
auth_value = values[key]
if auth_value.startswith("Bearer "):
authorization = auth_value[7:] # Remove "Bearer " prefix
else:
authorization = auth_value
else:
headers[key] = values[key]
return headers, authorization

View file

@ -48,7 +48,10 @@ class TavilySearchToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime, NeedsR
return provider_data.tavily_search_api_key return provider_data.tavily_search_api_key
async def list_runtime_tools( async def list_runtime_tools(
self, tool_group_id: str | None = None, mcp_endpoint: URL | None = None self,
tool_group_id: str | None = None,
mcp_endpoint: URL | None = None,
authorization: str | None = None,
) -> ListToolDefsResponse: ) -> ListToolDefsResponse:
return ListToolDefsResponse( return ListToolDefsResponse(
data=[ data=[
@ -69,7 +72,9 @@ class TavilySearchToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime, NeedsR
] ]
) )
async def invoke_tool(self, tool_name: str, kwargs: dict[str, Any]) -> ToolInvocationResult: async def invoke_tool(
self, tool_name: str, kwargs: dict[str, Any], authorization: str | None = None
) -> ToolInvocationResult:
api_key = self._get_api_key() api_key = self._get_api_key()
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
response = await client.post( response = await client.post(

View file

@ -49,7 +49,10 @@ class WolframAlphaToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime, NeedsR
return provider_data.wolfram_alpha_api_key return provider_data.wolfram_alpha_api_key
async def list_runtime_tools( async def list_runtime_tools(
self, tool_group_id: str | None = None, mcp_endpoint: URL | None = None self,
tool_group_id: str | None = None,
mcp_endpoint: URL | None = None,
authorization: str | None = None,
) -> ListToolDefsResponse: ) -> ListToolDefsResponse:
return ListToolDefsResponse( return ListToolDefsResponse(
data=[ data=[
@ -70,7 +73,9 @@ class WolframAlphaToolRuntimeImpl(ToolGroupsProtocolPrivate, ToolRuntime, NeedsR
] ]
) )
async def invoke_tool(self, tool_name: str, kwargs: dict[str, Any]) -> ToolInvocationResult: async def invoke_tool(
self, tool_name: str, kwargs: dict[str, Any], authorization: str | None = None
) -> ToolInvocationResult:
api_key = self._get_api_key() api_key = self._get_api_key()
params = { params = {
"input": kwargs["query"], "input": kwargs["query"],

View file

@ -30,6 +30,40 @@ from llama_stack_api import (
logger = get_logger(__name__, category="tools") logger = get_logger(__name__, category="tools")
def prepare_mcp_headers(base_headers: dict[str, str] | None, authorization: str | None) -> dict[str, str]:
"""
Prepare headers for MCP requests with authorization support.
Args:
base_headers: Base headers dictionary (can be None)
authorization: OAuth access token (without "Bearer " prefix)
Returns:
Headers dictionary with Authorization header if token provided
Raises:
ValueError: If Authorization header is specified in the headers dict (security risk)
"""
headers = dict(base_headers or {})
# Security check: reject any Authorization header in the headers dict
# Users must use the authorization parameter instead to avoid security risks
existing_keys_lower = {k.lower() for k in headers.keys()}
if "authorization" in existing_keys_lower:
raise ValueError(
"For security reasons, Authorization header cannot be passed via 'headers'. "
"Please use the 'authorization' parameter instead."
)
# Add Authorization header if token provided
if authorization:
# OAuth access token - add "Bearer " prefix
headers["Authorization"] = f"Bearer {authorization}"
return headers
protocol_cache = TTLDict(ttl_seconds=3600) protocol_cache = TTLDict(ttl_seconds=3600)
@ -112,9 +146,29 @@ async def client_wrapper(endpoint: str, headers: dict[str, str]) -> AsyncGenerat
raise raise
async def list_mcp_tools(endpoint: str, headers: dict[str, str]) -> ListToolDefsResponse: async def list_mcp_tools(
endpoint: str,
headers: dict[str, str] | None = None,
authorization: str | None = None,
) -> ListToolDefsResponse:
"""List tools available from an MCP server.
Args:
endpoint: MCP server endpoint URL
headers: Optional base headers to include
authorization: Optional OAuth access token (just the token, not "Bearer <token>")
Returns:
List of tool definitions from the MCP server
Raises:
ValueError: If Authorization is found in the headers parameter
"""
# Prepare headers with authorization handling
final_headers = prepare_mcp_headers(headers, authorization)
tools = [] tools = []
async with client_wrapper(endpoint, headers) as session: async with client_wrapper(endpoint, final_headers) as session:
tools_result = await session.list_tools() tools_result = await session.list_tools()
for tool in tools_result.tools: for tool in tools_result.tools:
tools.append( tools.append(
@ -132,9 +186,31 @@ async def list_mcp_tools(endpoint: str, headers: dict[str, str]) -> ListToolDefs
async def invoke_mcp_tool( async def invoke_mcp_tool(
endpoint: str, headers: dict[str, str], tool_name: str, kwargs: dict[str, Any] endpoint: str,
tool_name: str,
kwargs: dict[str, Any],
headers: dict[str, str] | None = None,
authorization: str | None = None,
) -> ToolInvocationResult: ) -> ToolInvocationResult:
async with client_wrapper(endpoint, headers) as session: """Invoke an MCP tool with the given arguments.
Args:
endpoint: MCP server endpoint URL
tool_name: Name of the tool to invoke
kwargs: Tool invocation arguments
headers: Optional base headers to include
authorization: Optional OAuth access token (just the token, not "Bearer <token>")
Returns:
Tool invocation result with content and error information
Raises:
ValueError: If Authorization header is found in the headers parameter
"""
# Prepare headers with authorization handling
final_headers = prepare_mcp_headers(headers, authorization)
async with client_wrapper(endpoint, final_headers) as session:
result = await session.call_tool(tool_name, kwargs) result = await session.call_tool(tool_name, kwargs)
content: list[InterleavedContentItem] = [] content: list[InterleavedContentItem] = []

View file

@ -609,14 +609,14 @@ def _combine_model_list_responses(endpoint: str, records: list[dict[str, Any]])
async def _patched_tool_invoke_method( async def _patched_tool_invoke_method(
original_method, provider_name: str, self, tool_name: str, kwargs: dict[str, Any] original_method, provider_name: str, self, tool_name: str, kwargs: dict[str, Any], authorization: str | None = None
): ):
"""Patched version of tool runtime invoke_tool method for recording/replay.""" """Patched version of tool runtime invoke_tool method for recording/replay."""
global _current_mode, _current_storage global _current_mode, _current_storage
if _current_mode == APIRecordingMode.LIVE or _current_storage is None: if _current_mode == APIRecordingMode.LIVE or _current_storage is None:
# Normal operation # Normal operation
return await original_method(self, tool_name, kwargs) return await original_method(self, tool_name, kwargs, authorization=authorization)
request_hash = normalize_tool_request(provider_name, tool_name, kwargs) request_hash = normalize_tool_request(provider_name, tool_name, kwargs)
@ -634,7 +634,7 @@ async def _patched_tool_invoke_method(
if _current_mode in (APIRecordingMode.RECORD, APIRecordingMode.RECORD_IF_MISSING): if _current_mode in (APIRecordingMode.RECORD, APIRecordingMode.RECORD_IF_MISSING):
# Make the tool call and record it # Make the tool call and record it
result = await original_method(self, tool_name, kwargs) result = await original_method(self, tool_name, kwargs, authorization=authorization)
request_data = { request_data = {
"test_id": get_test_context(), "test_id": get_test_context(),
@ -885,9 +885,11 @@ def patch_inference_clients():
OllamaAsyncClient.list = patched_ollama_list OllamaAsyncClient.list = patched_ollama_list
# Create patched methods for tool runtimes # Create patched methods for tool runtimes
async def patched_tavily_invoke_tool(self, tool_name: str, kwargs: dict[str, Any]): async def patched_tavily_invoke_tool(
self, tool_name: str, kwargs: dict[str, Any], authorization: str | None = None
):
return await _patched_tool_invoke_method( return await _patched_tool_invoke_method(
_original_methods["tavily_invoke_tool"], "tavily", self, tool_name, kwargs _original_methods["tavily_invoke_tool"], "tavily", self, tool_name, kwargs, authorization=authorization
) )
# Apply tool runtime patches # Apply tool runtime patches

View file

@ -490,6 +490,7 @@ class OpenAIResponseInputToolMCP(BaseModel):
:param server_label: Label to identify this MCP server :param server_label: Label to identify this MCP server
:param server_url: URL endpoint of the MCP server :param server_url: URL endpoint of the MCP server
:param headers: (Optional) HTTP headers to include when connecting to the server :param headers: (Optional) HTTP headers to include when connecting to the server
:param authorization: (Optional) OAuth access token for authenticating with the MCP server
:param require_approval: Approval requirement for tool calls ("always", "never", or filter) :param require_approval: Approval requirement for tool calls ("always", "never", or filter)
:param allowed_tools: (Optional) Restriction on which tools can be used from this server :param allowed_tools: (Optional) Restriction on which tools can be used from this server
""" """
@ -498,6 +499,7 @@ class OpenAIResponseInputToolMCP(BaseModel):
server_label: str server_label: str
server_url: str server_url: str
headers: dict[str, Any] | None = None headers: dict[str, Any] | None = None
authorization: str | None = Field(default=None, exclude=True)
require_approval: Literal["always"] | Literal["never"] | ApprovalFilter = "never" require_approval: Literal["always"] | Literal["never"] | ApprovalFilter = "never"
allowed_tools: list[str] | AllowedToolsFilter | None = None allowed_tools: list[str] | AllowedToolsFilter | None = None

View file

@ -196,22 +196,32 @@ class ToolRuntime(Protocol):
# TODO: This needs to be renamed once OPEN API generator name conflict issue is fixed. # TODO: This needs to be renamed once OPEN API generator name conflict issue is fixed.
@webmethod(route="/tool-runtime/list-tools", method="GET", level=LLAMA_STACK_API_V1) @webmethod(route="/tool-runtime/list-tools", method="GET", level=LLAMA_STACK_API_V1)
async def list_runtime_tools( async def list_runtime_tools(
self, tool_group_id: str | None = None, mcp_endpoint: URL | None = None self,
tool_group_id: str | None = None,
mcp_endpoint: URL | None = None,
authorization: str | None = None,
) -> ListToolDefsResponse: ) -> ListToolDefsResponse:
"""List all tools in the runtime. """List all tools in the runtime.
:param tool_group_id: The ID of the tool group to list tools for. :param tool_group_id: The ID of the tool group to list tools for.
:param mcp_endpoint: The MCP endpoint to use for the tool group. :param mcp_endpoint: The MCP endpoint to use for the tool group.
:param authorization: (Optional) OAuth access token for authenticating with the MCP server.
:returns: A ListToolDefsResponse. :returns: A ListToolDefsResponse.
""" """
... ...
@webmethod(route="/tool-runtime/invoke", method="POST", level=LLAMA_STACK_API_V1) @webmethod(route="/tool-runtime/invoke", method="POST", level=LLAMA_STACK_API_V1)
async def invoke_tool(self, tool_name: str, kwargs: dict[str, Any]) -> ToolInvocationResult: async def invoke_tool(
self,
tool_name: str,
kwargs: dict[str, Any],
authorization: str | None = None,
) -> ToolInvocationResult:
"""Run a tool with the given arguments. """Run a tool with the given arguments.
:param tool_name: The name of the tool to invoke. :param tool_name: The name of the tool to invoke.
:param kwargs: A dictionary of arguments to pass to the tool. :param kwargs: A dictionary of arguments to pass to the tool.
:param authorization: (Optional) OAuth access token for authenticating with the MCP server.
:returns: A ToolInvocationResult. :returns: A ToolInvocationResult.
""" """
... ...

View file

@ -193,7 +193,14 @@ class TestMCPToolsInChatCompletion:
mcp_endpoint=dict(uri=uri), mcp_endpoint=dict(uri=uri),
) )
provider_data = {"mcp_headers": {uri: {"Authorization": f"Bearer {AUTH_TOKEN}"}}} # Use old header-based approach for Phase 1 (backward compatibility)
provider_data = {
"mcp_headers": {
uri: {
"Authorization": f"Bearer {AUTH_TOKEN}",
},
},
}
auth_headers = { auth_headers = {
"X-LlamaStack-Provider-Data": json.dumps(provider_data), "X-LlamaStack-Provider-Data": json.dumps(provider_data),
} }

View file

@ -0,0 +1,614 @@
{
"test_id": "tests/integration/responses/test_mcp_authentication.py::test_mcp_authorization_backward_compatibility[openai_client-txt=openai/gpt-4o]",
"request": {
"method": "POST",
"url": "https://api.openai.com/v1/v1/chat/completions",
"headers": {},
"body": {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "What is the boiling point of myawesomeliquid?"
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_UeAsx9M8mAXo1F1LZj6TsEV9",
"type": "function",
"function": {
"name": "get_boiling_point",
"arguments": "{\"liquid_name\":\"myawesomeliquid\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_UeAsx9M8mAXo1F1LZj6TsEV9",
"content": [
{
"type": "text",
"text": "-100"
}
]
}
],
"stream": true,
"stream_options": {
"include_usage": true
},
"tools": [
{
"type": "function",
"function": {
"name": "greet_everyone",
"parameters": {
"properties": {
"url": {
"title": "Url",
"type": "string"
}
},
"required": [
"url"
],
"title": "greet_everyoneArguments",
"type": "object"
}
}
},
{
"type": "function",
"function": {
"name": "get_boiling_point",
"description": "\n Returns the boiling point of a liquid in Celsius or Fahrenheit.\n\n :param liquid_name: The name of the liquid\n :param celsius: Whether to return the boiling point in Celsius\n :return: The boiling point of the liquid in Celcius or Fahrenheit\n ",
"parameters": {
"properties": {
"liquid_name": {
"title": "Liquid Name",
"type": "string"
},
"celsius": {
"default": true,
"title": "Celsius",
"type": "boolean"
}
},
"required": [
"liquid_name"
],
"title": "get_boiling_pointArguments",
"type": "object"
}
}
}
]
},
"endpoint": "/v1/chat/completions",
"model": "gpt-4o"
},
"response": {
"body": [
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": "",
"function_call": null,
"refusal": null,
"role": "assistant",
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "c5g42LQpiBwmVH"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": "The",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "MEmQFjCKEsNDL"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": " boiling",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "dF3UemYO"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": " point",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "ENDOmjG37D"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": " of",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "6kb5u2d4ILV59"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": " \"",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "Y6Dp6rbT9OdBG"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": "my",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "EN0ShAkdxF2jIs"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": "aw",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "1NHavCOT2fSI63"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": "esom",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "VTwbnRFtKY2W"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": "eli",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "VJuNhLeGK43e6"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": "quid",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "bFgxcYCjU42I"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": "\"",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "5KR4mGTP0Rpu0O"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": " is",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "KCeY3i4Qo9L1j"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": " -",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "GgtT2kqCUk8jGH"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": "100",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "H3E18AkuuATh3"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": "\u00b0C",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "5kuUoomGw6aPf0"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": ".",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "CKIiDxWMV3zzcNj"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": "stop",
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "9KZoS4rawE"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-51e3ddbc9d23",
"choices": [],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": {
"completion_tokens": 17,
"prompt_tokens": 188,
"total_tokens": 205,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0
},
"prompt_tokens_details": {
"audio_tokens": 0,
"cached_tokens": 0
}
},
"obfuscation": "iq2ecCxqopvPO"
}
}
],
"is_streaming": true
},
"id_normalization_mapping": {}
}

View file

@ -0,0 +1,614 @@
{
"test_id": "tests/integration/responses/test_mcp_authentication.py::test_mcp_authorization_bearer[client_with_models-txt=openai/gpt-4o]",
"request": {
"method": "POST",
"url": "https://api.openai.com/v1/v1/chat/completions",
"headers": {},
"body": {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "What is the boiling point of myawesomeliquid?"
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_mitVYvmPaFfoSmKjzKo5xmZp",
"type": "function",
"function": {
"name": "get_boiling_point",
"arguments": "{\"liquid_name\":\"myawesomeliquid\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_mitVYvmPaFfoSmKjzKo5xmZp",
"content": [
{
"type": "text",
"text": "-100"
}
]
}
],
"stream": true,
"stream_options": {
"include_usage": true
},
"tools": [
{
"type": "function",
"function": {
"name": "greet_everyone",
"parameters": {
"properties": {
"url": {
"title": "Url",
"type": "string"
}
},
"required": [
"url"
],
"title": "greet_everyoneArguments",
"type": "object"
}
}
},
{
"type": "function",
"function": {
"name": "get_boiling_point",
"description": "\n Returns the boiling point of a liquid in Celsius or Fahrenheit.\n\n :param liquid_name: The name of the liquid\n :param celsius: Whether to return the boiling point in Celsius\n :return: The boiling point of the liquid in Celcius or Fahrenheit\n ",
"parameters": {
"properties": {
"liquid_name": {
"title": "Liquid Name",
"type": "string"
},
"celsius": {
"default": true,
"title": "Celsius",
"type": "boolean"
}
},
"required": [
"liquid_name"
],
"title": "get_boiling_pointArguments",
"type": "object"
}
}
}
]
},
"endpoint": "/v1/chat/completions",
"model": "gpt-4o"
},
"response": {
"body": [
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": "",
"function_call": null,
"refusal": null,
"role": "assistant",
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "veiGKPHTdRNcOX"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": "The",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "u9RK8eZYDguJs"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": " boiling",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "U0L1RjHF"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": " point",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "TMS6QVLJfj"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": " of",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "5zokjwZ0nBNlD"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": " \"",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "CmOp3DQRu0AqZ"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": "my",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "OlnZU0jlGyE2mD"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": "aw",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "PGCsCfw8zUqRAj"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": "esom",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "8P65fJ4x3QVF"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": "eli",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "HVTNGb62o54Ol"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": "quid",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "bdRgQioKQZM6"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": "\"",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "5djjyePEzwsPID"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": " is",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "xoN3TaCEum6A9"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": " -",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "UmU8LCL6WJIDrf"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": "100",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "FFXxvyme7JKyc"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": "\u00b0C",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "8BpDPmgFmIBJQQ"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": ".",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "Mey7rwshfBQbVlP"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": "stop",
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "IXaz4vn8As"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-5236eb1d546e",
"choices": [],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": {
"completion_tokens": 17,
"prompt_tokens": 188,
"total_tokens": 205,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0
},
"prompt_tokens_details": {
"audio_tokens": 0,
"cached_tokens": 0
}
},
"obfuscation": "9ebnd6bFXcdOY"
}
}
],
"is_streaming": true
},
"id_normalization_mapping": {}
}

View file

@ -0,0 +1,574 @@
{
"test_id": "tests/integration/responses/test_mcp_authentication.py::test_mcp_authorization_backward_compatibility[openai_client-txt=openai/gpt-4o]",
"request": {
"method": "POST",
"url": "https://api.openai.com/v1/v1/chat/completions",
"headers": {},
"body": {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "What is the boiling point of myawesomeliquid?"
}
],
"stream": true,
"stream_options": {
"include_usage": true
},
"tools": [
{
"type": "function",
"function": {
"name": "greet_everyone",
"parameters": {
"properties": {
"url": {
"title": "Url",
"type": "string"
}
},
"required": [
"url"
],
"title": "greet_everyoneArguments",
"type": "object"
}
}
},
{
"type": "function",
"function": {
"name": "get_boiling_point",
"description": "\n Returns the boiling point of a liquid in Celsius or Fahrenheit.\n\n :param liquid_name: The name of the liquid\n :param celsius: Whether to return the boiling point in Celsius\n :return: The boiling point of the liquid in Celcius or Fahrenheit\n ",
"parameters": {
"properties": {
"liquid_name": {
"title": "Liquid Name",
"type": "string"
},
"celsius": {
"default": true,
"title": "Celsius",
"type": "boolean"
}
},
"required": [
"liquid_name"
],
"title": "get_boiling_pointArguments",
"type": "object"
}
}
}
]
},
"endpoint": "/v1/chat/completions",
"model": "gpt-4o"
},
"response": {
"body": [
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": "assistant",
"tool_calls": [
{
"index": 0,
"id": "call_UeAsx9M8mAXo1F1LZj6TsEV9",
"function": {
"arguments": "",
"name": "get_boiling_point"
},
"type": "function"
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "bKe"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "{\"",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "kxw"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "li",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "cKkF"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "quid",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "md"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "_name",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "O"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "\":\"",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "o"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "my",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "nRfv"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "aw",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "1M8i"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "esom",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "7q"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "eli",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "R2Q"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "quid",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "lB"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "\"}",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "MDi"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": "tool_calls",
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "7KwE"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-56ddb450d815",
"choices": [],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": {
"completion_tokens": 22,
"prompt_tokens": 154,
"total_tokens": 176,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0
},
"prompt_tokens_details": {
"audio_tokens": 0,
"cached_tokens": 0
}
},
"obfuscation": "9IipvPESur5Y7"
}
}
],
"is_streaming": true
},
"id_normalization_mapping": {}
}

View file

@ -0,0 +1,614 @@
{
"test_id": "tests/integration/responses/test_mcp_authentication.py::test_mcp_authorization_bearer[openai_client-txt=openai/gpt-4o]",
"request": {
"method": "POST",
"url": "https://api.openai.com/v1/v1/chat/completions",
"headers": {},
"body": {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "What is the boiling point of myawesomeliquid?"
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_2lYntxgdJV66JFvD6OuICQCB",
"type": "function",
"function": {
"name": "get_boiling_point",
"arguments": "{\"liquid_name\":\"myawesomeliquid\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_2lYntxgdJV66JFvD6OuICQCB",
"content": [
{
"type": "text",
"text": "-100"
}
]
}
],
"stream": true,
"stream_options": {
"include_usage": true
},
"tools": [
{
"type": "function",
"function": {
"name": "greet_everyone",
"parameters": {
"properties": {
"url": {
"title": "Url",
"type": "string"
}
},
"required": [
"url"
],
"title": "greet_everyoneArguments",
"type": "object"
}
}
},
{
"type": "function",
"function": {
"name": "get_boiling_point",
"description": "\n Returns the boiling point of a liquid in Celsius or Fahrenheit.\n\n :param liquid_name: The name of the liquid\n :param celsius: Whether to return the boiling point in Celsius\n :return: The boiling point of the liquid in Celcius or Fahrenheit\n ",
"parameters": {
"properties": {
"liquid_name": {
"title": "Liquid Name",
"type": "string"
},
"celsius": {
"default": true,
"title": "Celsius",
"type": "boolean"
}
},
"required": [
"liquid_name"
],
"title": "get_boiling_pointArguments",
"type": "object"
}
}
}
]
},
"endpoint": "/v1/chat/completions",
"model": "gpt-4o"
},
"response": {
"body": [
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": "",
"function_call": null,
"refusal": null,
"role": "assistant",
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "BNpFmbWkpYEjZX"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": "The",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "HdnyHcq2CLvjn"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": " boiling",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "gOMuwgrp"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": " point",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "OTfqq7Yggw"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": " of",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "cwJMhZJyf5PIp"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": " \"",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "54NR7IGiuBTw5"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": "my",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "q1x9cVVPTflQti"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": "aw",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "vcudLe3yaadkvB"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": "esom",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "uql1pBt4elRL"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": "eli",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "M2kzUEkJctjYp"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": "quid",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "Waet2ux2zs9P"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": "\"",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "KjbjxdGYUZDuiI"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": " is",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "Fg8IXJhJv8iAI"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": " -",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "wiAqPLAoinVhQq"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": "100",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "vJnb9sE969jph"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": "\u00b0C",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "5Hgi5CU0aV0sPw"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": ".",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "RDfKhuQo4E4TLXU"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": "stop",
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "oN1EYVkDbW"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-59faeeca84b1",
"choices": [],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": {
"completion_tokens": 17,
"prompt_tokens": 188,
"total_tokens": 205,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0
},
"prompt_tokens_details": {
"audio_tokens": 0,
"cached_tokens": 0
}
},
"obfuscation": "OfhOTT3VdJ2s7"
}
}
],
"is_streaming": true
},
"id_normalization_mapping": {}
}

View file

@ -0,0 +1,574 @@
{
"test_id": "tests/integration/responses/test_mcp_authentication.py::test_mcp_authorization_bearer[openai_client-txt=openai/gpt-4o]",
"request": {
"method": "POST",
"url": "https://api.openai.com/v1/v1/chat/completions",
"headers": {},
"body": {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "What is the boiling point of myawesomeliquid?"
}
],
"stream": true,
"stream_options": {
"include_usage": true
},
"tools": [
{
"type": "function",
"function": {
"name": "greet_everyone",
"parameters": {
"properties": {
"url": {
"title": "Url",
"type": "string"
}
},
"required": [
"url"
],
"title": "greet_everyoneArguments",
"type": "object"
}
}
},
{
"type": "function",
"function": {
"name": "get_boiling_point",
"description": "\n Returns the boiling point of a liquid in Celsius or Fahrenheit.\n\n :param liquid_name: The name of the liquid\n :param celsius: Whether to return the boiling point in Celsius\n :return: The boiling point of the liquid in Celcius or Fahrenheit\n ",
"parameters": {
"properties": {
"liquid_name": {
"title": "Liquid Name",
"type": "string"
},
"celsius": {
"default": true,
"title": "Celsius",
"type": "boolean"
}
},
"required": [
"liquid_name"
],
"title": "get_boiling_pointArguments",
"type": "object"
}
}
}
]
},
"endpoint": "/v1/chat/completions",
"model": "gpt-4o"
},
"response": {
"body": [
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": "assistant",
"tool_calls": [
{
"index": 0,
"id": "call_2lYntxgdJV66JFvD6OuICQCB",
"function": {
"arguments": "",
"name": "get_boiling_point"
},
"type": "function"
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "UmB"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "{\"",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "ejb"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "li",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "Loxj"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "quid",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "IQ"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "_name",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "8"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "\":\"",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "G"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "my",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "lo9p"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "aw",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "YWPA"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "esom",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "vV"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "eli",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "e0t"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "quid",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "kv"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "\"}",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "h2F"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": "tool_calls",
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "B9QY"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-775a161a318a",
"choices": [],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": {
"completion_tokens": 22,
"prompt_tokens": 154,
"total_tokens": 176,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0
},
"prompt_tokens_details": {
"audio_tokens": 0,
"cached_tokens": 0
}
},
"obfuscation": "MH88zIptmy2Xs"
}
}
],
"is_streaming": true
},
"id_normalization_mapping": {}
}

View file

@ -0,0 +1,574 @@
{
"test_id": "tests/integration/responses/test_mcp_authentication.py::test_mcp_authorization_bearer[client_with_models-txt=openai/gpt-4o]",
"request": {
"method": "POST",
"url": "https://api.openai.com/v1/v1/chat/completions",
"headers": {},
"body": {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "What is the boiling point of myawesomeliquid?"
}
],
"stream": true,
"stream_options": {
"include_usage": true
},
"tools": [
{
"type": "function",
"function": {
"name": "greet_everyone",
"parameters": {
"properties": {
"url": {
"title": "Url",
"type": "string"
}
},
"required": [
"url"
],
"title": "greet_everyoneArguments",
"type": "object"
}
}
},
{
"type": "function",
"function": {
"name": "get_boiling_point",
"description": "\n Returns the boiling point of a liquid in Celsius or Fahrenheit.\n\n :param liquid_name: The name of the liquid\n :param celsius: Whether to return the boiling point in Celsius\n :return: The boiling point of the liquid in Celcius or Fahrenheit\n ",
"parameters": {
"properties": {
"liquid_name": {
"title": "Liquid Name",
"type": "string"
},
"celsius": {
"default": true,
"title": "Celsius",
"type": "boolean"
}
},
"required": [
"liquid_name"
],
"title": "get_boiling_pointArguments",
"type": "object"
}
}
}
]
},
"endpoint": "/v1/chat/completions",
"model": "gpt-4o"
},
"response": {
"body": [
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": "assistant",
"tool_calls": [
{
"index": 0,
"id": "call_mitVYvmPaFfoSmKjzKo5xmZp",
"function": {
"arguments": "",
"name": "get_boiling_point"
},
"type": "function"
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "5Y1"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "{\"",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "QzQ"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "li",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "4NPm"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "quid",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "Lh"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "_name",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "r"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "\":\"",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "w"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "my",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "GSVa"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "aw",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "AWZm"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "esom",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "DG"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "eli",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "1Bw"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "quid",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "Oq"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "\"}",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "cI8"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": "tool_calls",
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "kKqh"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c84e894f47a6",
"choices": [],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": {
"completion_tokens": 22,
"prompt_tokens": 154,
"total_tokens": 176,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0
},
"prompt_tokens_details": {
"audio_tokens": 0,
"cached_tokens": 0
}
},
"obfuscation": "etTUytEvlkJ99"
}
}
],
"is_streaming": true
},
"id_normalization_mapping": {}
}

View file

@ -0,0 +1,574 @@
{
"test_id": "tests/integration/responses/test_mcp_authentication.py::test_mcp_authorization_backward_compatibility[client_with_models-txt=openai/gpt-4o]",
"request": {
"method": "POST",
"url": "https://api.openai.com/v1/v1/chat/completions",
"headers": {},
"body": {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "What is the boiling point of myawesomeliquid?"
}
],
"stream": true,
"stream_options": {
"include_usage": true
},
"tools": [
{
"type": "function",
"function": {
"name": "greet_everyone",
"parameters": {
"properties": {
"url": {
"title": "Url",
"type": "string"
}
},
"required": [
"url"
],
"title": "greet_everyoneArguments",
"type": "object"
}
}
},
{
"type": "function",
"function": {
"name": "get_boiling_point",
"description": "\n Returns the boiling point of a liquid in Celsius or Fahrenheit.\n\n :param liquid_name: The name of the liquid\n :param celsius: Whether to return the boiling point in Celsius\n :return: The boiling point of the liquid in Celcius or Fahrenheit\n ",
"parameters": {
"properties": {
"liquid_name": {
"title": "Liquid Name",
"type": "string"
},
"celsius": {
"default": true,
"title": "Celsius",
"type": "boolean"
}
},
"required": [
"liquid_name"
],
"title": "get_boiling_pointArguments",
"type": "object"
}
}
}
]
},
"endpoint": "/v1/chat/completions",
"model": "gpt-4o"
},
"response": {
"body": [
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": "assistant",
"tool_calls": [
{
"index": 0,
"id": "call_wnbihJuwYAfnI8uxy84Yl48j",
"function": {
"arguments": "",
"name": "get_boiling_point"
},
"type": "function"
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "TC0"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "{\"",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "hDL"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "li",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "4G8Z"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "quid",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "ow"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "_name",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "P"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "\":\"",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "M"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "my",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "yhAk"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "aw",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "SdIN"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "esom",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "2z"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "eli",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "nEC"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "quid",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "2B"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": [
{
"index": 0,
"id": null,
"function": {
"arguments": "\"}",
"name": null
},
"type": null
}
]
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "DoL"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": "tool_calls",
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "cSRf"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-c9c723cd0123",
"choices": [],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": {
"completion_tokens": 22,
"prompt_tokens": 154,
"total_tokens": 176,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0
},
"prompt_tokens_details": {
"audio_tokens": 0,
"cached_tokens": 0
}
},
"obfuscation": "ejlSF0NzXFFso"
}
}
],
"is_streaming": true
},
"id_normalization_mapping": {}
}

View file

@ -0,0 +1,614 @@
{
"test_id": "tests/integration/responses/test_mcp_authentication.py::test_mcp_authorization_backward_compatibility[client_with_models-txt=openai/gpt-4o]",
"request": {
"method": "POST",
"url": "https://api.openai.com/v1/v1/chat/completions",
"headers": {},
"body": {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "What is the boiling point of myawesomeliquid?"
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_wnbihJuwYAfnI8uxy84Yl48j",
"type": "function",
"function": {
"name": "get_boiling_point",
"arguments": "{\"liquid_name\":\"myawesomeliquid\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_wnbihJuwYAfnI8uxy84Yl48j",
"content": [
{
"type": "text",
"text": "-100"
}
]
}
],
"stream": true,
"stream_options": {
"include_usage": true
},
"tools": [
{
"type": "function",
"function": {
"name": "greet_everyone",
"parameters": {
"properties": {
"url": {
"title": "Url",
"type": "string"
}
},
"required": [
"url"
],
"title": "greet_everyoneArguments",
"type": "object"
}
}
},
{
"type": "function",
"function": {
"name": "get_boiling_point",
"description": "\n Returns the boiling point of a liquid in Celsius or Fahrenheit.\n\n :param liquid_name: The name of the liquid\n :param celsius: Whether to return the boiling point in Celsius\n :return: The boiling point of the liquid in Celcius or Fahrenheit\n ",
"parameters": {
"properties": {
"liquid_name": {
"title": "Liquid Name",
"type": "string"
},
"celsius": {
"default": true,
"title": "Celsius",
"type": "boolean"
}
},
"required": [
"liquid_name"
],
"title": "get_boiling_pointArguments",
"type": "object"
}
}
}
]
},
"endpoint": "/v1/chat/completions",
"model": "gpt-4o"
},
"response": {
"body": [
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": "",
"function_call": null,
"refusal": null,
"role": "assistant",
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "Usdowqbd6beiYB"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": "The",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "nVevItSH27TBR"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": " boiling",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "HWyYtVAl"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": " point",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "kvvcut6Eib"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": " of",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "E0osAbGBpCPvy"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": " \"",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "GmH7m44fmv0Mk"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": "my",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "oJ4DV7z5GiqJqX"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": "aw",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "8AmNNAYPXMNrEr"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": "esom",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "JEzK8X8AD9hP"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": "eli",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "8EGj5LyQzpZMt"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": "quid",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "wQG19uBuvC7j"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": "\"",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "8Wyenb7E997f9E"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": " is",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "SVXiel7RHA6f3"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": " -",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "ynScunJEjmOWBo"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": "100",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "po2PLlPavc9TN"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": "\u00b0C",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "mt2jiL22pWkH93"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": ".",
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": null,
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "32gJJ61zmjmftOn"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [
{
"delta": {
"content": null,
"function_call": null,
"refusal": null,
"role": null,
"tool_calls": null
},
"finish_reason": "stop",
"index": 0,
"logprobs": null
}
],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": null,
"obfuscation": "HszNIiCJ12"
}
},
{
"__type__": "openai.types.chat.chat_completion_chunk.ChatCompletionChunk",
"__data__": {
"id": "rec-db81127157a8",
"choices": [],
"created": 0,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion.chunk",
"service_tier": "default",
"system_fingerprint": "fp_cbf1785567",
"usage": {
"completion_tokens": 17,
"prompt_tokens": 188,
"total_tokens": 205,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0
},
"prompt_tokens_details": {
"audio_tokens": 0,
"cached_tokens": 0
}
},
"obfuscation": "cAx3IDg7toBDJ"
}
}
],
"is_streaming": true
},
"id_normalization_mapping": {}
}

View file

@ -88,6 +88,7 @@ class TestConversationResponses:
assert "apple" in response.output_text.lower() assert "apple" in response.output_text.lower()
@pytest.mark.timeout(60, method="thread")
def test_conversation_error_handling(self, openai_client, text_model_id): def test_conversation_error_handling(self, openai_client, text_model_id):
"""Test error handling for invalid and nonexistent conversations.""" """Test error handling for invalid and nonexistent conversations."""
# Invalid conversation ID format # Invalid conversation ID format

View file

@ -0,0 +1,105 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
import pytest
from tests.common.mcp import make_mcp_server
from .helpers import setup_mcp_tools
# MCP authentication tests with recordings
# Tests for bearer token authorization support in MCP tool configurations
def test_mcp_authorization_bearer(responses_client, text_model_id):
"""Test that bearer authorization is correctly applied to MCP requests."""
test_token = "test-bearer-token-789"
with make_mcp_server(required_auth_token=test_token) as mcp_server_info:
tools = setup_mcp_tools(
[
{
"type": "mcp",
"server_label": "auth-mcp",
"server_url": "<FILLED_BY_TEST_RUNNER>",
"authorization": test_token, # Just the token, not "Bearer <token>"
}
],
mcp_server_info,
)
# Create response - authorization should be applied
response = responses_client.responses.create(
model=text_model_id,
input="What is the boiling point of myawesomeliquid?",
tools=tools,
stream=False,
)
# Verify list_tools succeeded (requires auth)
assert len(response.output) >= 3
assert response.output[0].type == "mcp_list_tools"
assert len(response.output[0].tools) == 2
# Verify tool invocation succeeded (requires auth)
assert response.output[1].type == "mcp_call"
assert response.output[1].error is None
def test_mcp_authorization_error_when_header_provided(responses_client, text_model_id):
"""Test that providing Authorization in headers raises a security error."""
test_token = "test-token-123"
with make_mcp_server(required_auth_token=test_token) as mcp_server_info:
tools = setup_mcp_tools(
[
{
"type": "mcp",
"server_label": "header-auth-mcp",
"server_url": "<FILLED_BY_TEST_RUNNER>",
"headers": {"Authorization": f"Bearer {test_token}"}, # Security risk - should be rejected
}
],
mcp_server_info,
)
# Create response - should raise BadRequestError for security reasons
with pytest.raises((ValueError, Exception), match="Authorization header cannot be passed via 'headers'"):
responses_client.responses.create(
model=text_model_id,
input="What is the boiling point of myawesomeliquid?",
tools=tools,
stream=False,
)
def test_mcp_authorization_backward_compatibility(responses_client, text_model_id):
"""Test that MCP tools work without authorization (backward compatibility)."""
# No authorization required
with make_mcp_server(required_auth_token=None) as mcp_server_info:
tools = setup_mcp_tools(
[
{
"type": "mcp",
"server_label": "noauth-mcp",
"server_url": "<FILLED_BY_TEST_RUNNER>",
}
],
mcp_server_info,
)
# Create response without authorization
response = responses_client.responses.create(
model=text_model_id,
input="What is the boiling point of myawesomeliquid?",
tools=tools,
stream=False,
)
# Verify operations succeeded without auth
assert len(response.output) >= 3
assert response.output[0].type == "mcp_list_tools"
assert response.output[1].type == "mcp_call"
assert response.output[1].error is None

View file

@ -249,7 +249,7 @@ def test_response_non_streaming_mcp_tool(responses_client, text_model_id, case,
for tool in tools: for tool in tools:
if tool["type"] == "mcp": if tool["type"] == "mcp":
tool["headers"] = {"Authorization": "Bearer test-token"} tool["authorization"] = "test-token"
response = responses_client.responses.create( response = responses_client.responses.create(
model=text_model_id, model=text_model_id,

View file

@ -37,6 +37,7 @@ def test_mcp_invocation(llama_stack_client, text_model_id, mcp_server):
mcp_endpoint=dict(uri=uri), mcp_endpoint=dict(uri=uri),
) )
# Use old header-based approach for Phase 1 (backward compatibility)
provider_data = { provider_data = {
"mcp_headers": { "mcp_headers": {
uri: { uri: {
@ -53,7 +54,7 @@ def test_mcp_invocation(llama_stack_client, text_model_id, mcp_server):
tools_list = llama_stack_client.tools.list( tools_list = llama_stack_client.tools.list(
toolgroup_id=test_toolgroup_id, toolgroup_id=test_toolgroup_id,
extra_headers=auth_headers, extra_headers=auth_headers, # Use old header-based approach
) )
assert len(tools_list) == 2 assert len(tools_list) == 2
assert {t.name for t in tools_list} == {"greet_everyone", "get_boiling_point"} assert {t.name for t in tools_list} == {"greet_everyone", "get_boiling_point"}
@ -61,7 +62,7 @@ def test_mcp_invocation(llama_stack_client, text_model_id, mcp_server):
response = llama_stack_client.tool_runtime.invoke_tool( response = llama_stack_client.tool_runtime.invoke_tool(
tool_name="greet_everyone", tool_name="greet_everyone",
kwargs=dict(url="https://www.google.com"), kwargs=dict(url="https://www.google.com"),
extra_headers=auth_headers, extra_headers=auth_headers, # Use old header-based approach
) )
content = response.content content = response.content
assert len(content) == 1 assert len(content) == 1
@ -76,9 +77,7 @@ def test_mcp_invocation(llama_stack_client, text_model_id, mcp_server):
"server_label": test_toolgroup_id, "server_label": test_toolgroup_id,
"require_approval": "never", "require_approval": "never",
"allowed_tools": [tool.name for tool in tools_list], "allowed_tools": [tool.name for tool in tools_list],
"headers": { "authorization": AUTH_TOKEN,
"Authorization": f"Bearer {AUTH_TOKEN}",
},
} }
] ]
agent = Agent( agent = Agent(
@ -104,7 +103,6 @@ def test_mcp_invocation(llama_stack_client, text_model_id, mcp_server):
} }
], ],
stream=True, stream=True,
extra_headers=auth_headers,
) )
) )
events = [chunk.event for chunk in chunks] events = [chunk.event for chunk in chunks]

View file

@ -4,8 +4,7 @@
# This source code is licensed under the terms described in the LICENSE file in # This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree. # the root directory of this source tree.
""" """Integration tests for MCP tools with complex JSON Schema support.
Integration tests for MCP tools with complex JSON Schema support.
Tests $ref, $defs, and other JSON Schema features through MCP integration. Tests $ref, $defs, and other JSON Schema features through MCP integration.
""" """
@ -123,7 +122,14 @@ class TestMCPSchemaPreservation:
mcp_endpoint=dict(uri=uri), mcp_endpoint=dict(uri=uri),
) )
provider_data = {"mcp_headers": {uri: {"Authorization": f"Bearer {AUTH_TOKEN}"}}} # Use old header-based approach for Phase 1 (backward compatibility)
provider_data = {
"mcp_headers": {
uri: {
"Authorization": f"Bearer {AUTH_TOKEN}",
},
},
}
auth_headers = { auth_headers = {
"X-LlamaStack-Provider-Data": json.dumps(provider_data), "X-LlamaStack-Provider-Data": json.dumps(provider_data),
} }
@ -166,7 +172,15 @@ class TestMCPSchemaPreservation:
provider_id="model-context-protocol", provider_id="model-context-protocol",
mcp_endpoint=dict(uri=uri), mcp_endpoint=dict(uri=uri),
) )
provider_data = {"mcp_headers": {uri: {"Authorization": f"Bearer {AUTH_TOKEN}"}}}
# Use old header-based approach for Phase 1 (backward compatibility)
provider_data = {
"mcp_headers": {
uri: {
"Authorization": f"Bearer {AUTH_TOKEN}",
},
},
}
auth_headers = { auth_headers = {
"X-LlamaStack-Provider-Data": json.dumps(provider_data), "X-LlamaStack-Provider-Data": json.dumps(provider_data),
} }
@ -216,7 +230,14 @@ class TestMCPSchemaPreservation:
mcp_endpoint=dict(uri=uri), mcp_endpoint=dict(uri=uri),
) )
provider_data = {"mcp_headers": {uri: {"Authorization": f"Bearer {AUTH_TOKEN}"}}} # Use old header-based approach for Phase 1 (backward compatibility)
provider_data = {
"mcp_headers": {
uri: {
"Authorization": f"Bearer {AUTH_TOKEN}",
},
},
}
auth_headers = { auth_headers = {
"X-LlamaStack-Provider-Data": json.dumps(provider_data), "X-LlamaStack-Provider-Data": json.dumps(provider_data),
} }
@ -263,7 +284,14 @@ class TestMCPToolInvocation:
mcp_endpoint=dict(uri=uri), mcp_endpoint=dict(uri=uri),
) )
provider_data = {"mcp_headers": {uri: {"Authorization": f"Bearer {AUTH_TOKEN}"}}} # Use old header-based approach for Phase 1 (backward compatibility)
provider_data = {
"mcp_headers": {
uri: {
"Authorization": f"Bearer {AUTH_TOKEN}",
},
},
}
auth_headers = { auth_headers = {
"X-LlamaStack-Provider-Data": json.dumps(provider_data), "X-LlamaStack-Provider-Data": json.dumps(provider_data),
} }
@ -309,7 +337,14 @@ class TestMCPToolInvocation:
mcp_endpoint=dict(uri=uri), mcp_endpoint=dict(uri=uri),
) )
provider_data = {"mcp_headers": {uri: {"Authorization": f"Bearer {AUTH_TOKEN}"}}} # Use old header-based approach for Phase 1 (backward compatibility)
provider_data = {
"mcp_headers": {
uri: {
"Authorization": f"Bearer {AUTH_TOKEN}",
},
},
}
auth_headers = { auth_headers = {
"X-LlamaStack-Provider-Data": json.dumps(provider_data), "X-LlamaStack-Provider-Data": json.dumps(provider_data),
} }
@ -365,7 +400,14 @@ class TestAgentWithMCPTools:
mcp_endpoint=dict(uri=uri), mcp_endpoint=dict(uri=uri),
) )
provider_data = {"mcp_headers": {uri: {"Authorization": f"Bearer {AUTH_TOKEN}"}}} # Use old header-based approach for Phase 1 (backward compatibility)
provider_data = {
"mcp_headers": {
uri: {
"Authorization": f"Bearer {AUTH_TOKEN}",
},
},
}
auth_headers = { auth_headers = {
"X-LlamaStack-Provider-Data": json.dumps(provider_data), "X-LlamaStack-Provider-Data": json.dumps(provider_data),
} }
@ -381,6 +423,7 @@ class TestAgentWithMCPTools:
"server_label": test_toolgroup_id, "server_label": test_toolgroup_id,
"require_approval": "never", "require_approval": "never",
"allowed_tools": [tool.name for tool in tools_list], "allowed_tools": [tool.name for tool in tools_list],
"authorization": AUTH_TOKEN,
} }
] ]
@ -389,7 +432,6 @@ class TestAgentWithMCPTools:
model=text_model_id, model=text_model_id,
instructions="You are a helpful assistant that can process orders and book flights.", instructions="You are a helpful assistant that can process orders and book flights.",
tools=tool_defs, tools=tool_defs,
extra_headers=auth_headers,
) )
session_id = agent.create_session("test-session-complex") session_id = agent.create_session("test-session-complex")
@ -411,7 +453,6 @@ class TestAgentWithMCPTools:
} }
], ],
stream=True, stream=True,
extra_headers=auth_headers,
) )
) )

View file

@ -137,7 +137,7 @@ class ToolGroupsImpl(Impl):
async def unregister_toolgroup(self, toolgroup_id: str): async def unregister_toolgroup(self, toolgroup_id: str):
return toolgroup_id return toolgroup_id
async def list_runtime_tools(self, toolgroup_id, mcp_endpoint): async def list_runtime_tools(self, toolgroup_id, mcp_endpoint, authorization=None):
return ListToolDefsResponse( return ListToolDefsResponse(
data=[ data=[
ToolDef( ToolDef(