feat(mcp): support dynamic HTTP request headers in MCPClient (#37938)

This commit is contained in:
Sanket Shakya 2026-06-30 08:58:24 +05:30 committed by GitHub
parent 995ba6b00e
commit 9521c7fe7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 67 additions and 1 deletions

View File

@ -1,10 +1,13 @@
import logging
import re
from collections.abc import Callable
from contextlib import AbstractContextManager, ExitStack
from types import TracebackType
from typing import Any
from urllib.parse import urlparse
from flask import has_request_context, request
from core.mcp.client.sse_client import sse_client
from core.mcp.client.streamable_client import streamablehttp_client
from core.mcp.error import MCPConnectionError
@ -23,10 +26,22 @@ class MCPClient:
sse_read_timeout: float | None = None,
):
self.server_url = server_url
self.headers = headers or {}
self.headers = headers.copy() if headers else {}
self.timeout = timeout
self.sse_read_timeout = sse_read_timeout
# Substitute placeholders with incoming request headers if in a request context
if has_request_context() and self.headers:
pattern = re.compile(r"\{\{\s*request\.headers?\.(.+?)\s*\}\}", re.IGNORECASE)
for key, value in list(self.headers.items()):
if isinstance(value, str):
def replace_func(match):
header_name = match.group(1)
return request.headers.get(header_name, "")
self.headers[key] = pattern.sub(replace_func, value)
# Initialize session and client objects
self._session: ClientSession | None = None
self._exit_stack = ExitStack()

View File

@ -43,6 +43,57 @@ class TestMCPClient:
assert client.timeout is None
assert client.sse_read_timeout is None
def test_init_with_dynamic_request_headers(self):
"""Test client initialization with dynamic request headers."""
from flask import Flask
app = Flask("test")
with app.test_request_context(headers={"X-Custom-Auth": "my-secret-token", "X-User-Id": "user123"}):
client = MCPClient(
server_url="http://test.example.com",
headers={
"Authorization": "Bearer {{request.headers.X-Custom-Auth}}",
"X-Request-User": "{{request.header.X-User-Id}}",
"X-Static-Header": "static-val",
},
)
assert client.headers == {
"Authorization": "Bearer my-secret-token",
"X-Request-User": "user123",
"X-Static-Header": "static-val",
}
def test_init_with_dynamic_request_headers_missing(self):
"""Test client initialization with dynamic request headers that are missing."""
from flask import Flask
app = Flask("test")
with app.test_request_context(headers={}):
client = MCPClient(
server_url="http://test.example.com",
headers={
"Authorization": "Bearer {{request.headers.X-Custom-Auth}}",
},
)
assert client.headers == {
"Authorization": "Bearer ",
}
def test_init_with_dynamic_request_headers_no_context(self):
"""Test client initialization with dynamic request headers but no request context."""
client = MCPClient(
server_url="http://test.example.com",
headers={
"Authorization": "Bearer {{request.headers.X-Custom-Auth}}",
},
)
assert client.headers == {
"Authorization": "Bearer {{request.headers.X-Custom-Auth}}",
}
@patch("core.mcp.mcp_client.streamablehttp_client")
@patch("core.mcp.mcp_client.ClientSession")
def test_initialize_with_mcp_url(self, mock_client_session, mock_streamable_client):