Skip to content

Commit e373644

Browse files
authored
Langchain MCP Adapters Fix (#245)
1 parent 4d1e210 commit e373644

3 files changed

Lines changed: 54 additions & 0 deletions

File tree

databricks_mcp/src/databricks_mcp/oauth_provider.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class DatabricksOAuthClientProvider(OAuthClientProvider):
4646
"""
4747

4848
def __init__(self, workspace_client: WorkspaceClient):
49+
self.workspace_client = workspace_client
4950
self.databricks_token_storage = DatabricksTokenStorage(workspace_client)
5051

5152
super().__init__(

integrations/langchain/src/databricks_langchain/multi_server_mcp_client.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,39 @@
11
from typing import Any, Callable, List, Union
22

3+
import httpx
34
from databricks.sdk import WorkspaceClient
45
from databricks_mcp.oauth_provider import DatabricksOAuthClientProvider
56
from langchain_mcp_adapters.client import MultiServerMCPClient
7+
from langchain_mcp_adapters.sessions import McpHttpClientFactory
68
from pydantic import BaseModel, ConfigDict, Field
79

810

11+
class DatabricksMcpHttpClientFactory(McpHttpClientFactory):
12+
def __call__(
13+
self,
14+
headers: dict[str, str] | None = None,
15+
timeout: httpx.Timeout | None = None,
16+
auth: httpx.Auth | None = None,
17+
) -> httpx.AsyncClient:
18+
if isinstance(auth, DatabricksOAuthClientProvider) and auth.workspace_client is not None:
19+
# Currently DatabricksOAuthClientProvider does not do a full U2M.
20+
# Therefore a new fresh token is only retrieved on the first initialization.
21+
# As this factory is called for each request, we are reinitailizing the
22+
# DatabricksOAuthClientProvider with the original workspace client to get a new token
23+
24+
return httpx.AsyncClient(
25+
headers=headers,
26+
timeout=timeout,
27+
auth=DatabricksOAuthClientProvider(auth.workspace_client),
28+
)
29+
else:
30+
return httpx.AsyncClient(
31+
headers=headers,
32+
timeout=timeout,
33+
auth=auth,
34+
)
35+
36+
937
class MCPServer(BaseModel):
1038
"""
1139
Base configuration for an MCP server connection using streamable HTTP transport.
@@ -234,6 +262,7 @@ def to_connection_dict(self) -> dict[str, Any]:
234262

235263
# Add Databricks auth provider
236264
data["auth"] = self._auth_provider
265+
data["httpx_client_factory"] = DatabricksMcpHttpClientFactory()
237266

238267
return data
239268

integrations/langchain/tests/unit_tests/test_multi_server_mcp_client.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from databricks.sdk import WorkspaceClient
99

1010
from databricks_langchain.multi_server_mcp_client import (
11+
DatabricksMcpHttpClientFactory,
1112
DatabricksMCPServer,
1213
DatabricksMultiServerMCPClient,
1314
MCPServer,
@@ -144,6 +145,29 @@ def test_databricks_server_accepts_extra_params(self):
144145
assert connection_dict["timeout"] == 45.0
145146
assert connection_dict["headers"] == {"X-Custom": "header"}
146147

148+
def test_databricks_server_includes_http_factory(self):
149+
"""Test that DatabricksMCPServer includes the custom HTTP client factory."""
150+
mock_workspace_client = create_autospec(WorkspaceClient, instance=True)
151+
152+
with patch(
153+
"databricks_langchain.multi_server_mcp_client.DatabricksOAuthClientProvider"
154+
) as mock_auth:
155+
mock_auth_instance = MagicMock()
156+
mock_auth.return_value = mock_auth_instance
157+
158+
server = DatabricksMCPServer(
159+
name="databricks",
160+
url="https://databricks.com/mcp",
161+
workspace_client=mock_workspace_client,
162+
)
163+
164+
connection_dict = server.to_connection_dict()
165+
166+
assert "httpx_client_factory" in connection_dict
167+
assert isinstance(
168+
connection_dict["httpx_client_factory"], DatabricksMcpHttpClientFactory
169+
)
170+
147171

148172
class TestDatabricksMultiServerMCPClient:
149173
"""Tests for the DatabricksMultiServerMCPClient class."""

0 commit comments

Comments
 (0)