Skip to content

Commit 7eb4098

Browse files
authored
chore: more linting (#261)
A bit more linting for line length
1 parent 54c0a76 commit 7eb4098

File tree

15 files changed

+202
-67
lines changed

15 files changed

+202
-67
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ lint lint_diff:
3131
# [ "$(PYTHON_FILES)" = "" ] || uv run mypy $(PYTHON_FILES)
3232

3333
format format_diff:
34-
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --fix $(PYTHON_FILES)
3534
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES)
35+
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --fix $(PYTHON_FILES)
3636

3737

3838

langchain_mcp_adapters/client.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Client for connecting to multiple MCP servers and loading LangChain-compatible resources.
1+
"""Client for connecting to multiple MCP servers and loading LangChain tools/resources.
22
33
This module provides the MultiServerMCPClient class for managing connections to multiple
44
MCP servers and loading tools, prompts, and resources from them.
@@ -29,7 +29,8 @@
2929
from langchain_mcp_adapters.tools import load_mcp_tools
3030

3131
ASYNC_CONTEXT_MANAGER_ERROR = (
32-
"As of langchain-mcp-adapters 0.1.0, MultiServerMCPClient cannot be used as a context manager (e.g., async with MultiServerMCPClient(...)). "
32+
"As of langchain-mcp-adapters 0.1.0, MultiServerMCPClient cannot be used as a "
33+
"context manager (e.g., async with MultiServerMCPClient(...)). "
3334
"Instead, you can do one of the following:\n"
3435
"1. client = MultiServerMCPClient(...)\n"
3536
" tools = await client.get_tools()\n"
@@ -40,7 +41,10 @@
4041

4142

4243
class MultiServerMCPClient:
43-
"""Client for connecting to multiple MCP servers and loading LangChain-compatible tools, prompts and resources from them."""
44+
"""Client for connecting to multiple MCP servers.
45+
46+
Loads LangChain-compatible tools, prompts and resources from MCP servers.
47+
"""
4448

4549
def __init__(self, connections: dict[str, Connection] | None = None) -> None:
4650
"""Initialize a MultiServerMCPClient with MCP servers connections.
@@ -58,7 +62,8 @@ def __init__(self, connections: dict[str, Connection] | None = None) -> None:
5862
{
5963
"math": {
6064
"command": "python",
61-
# Make sure to update to the full absolute path to your math_server.py file
65+
# Make sure to update to the full absolute path to your
66+
# math_server.py file
6267
"args": ["/path/to/math_server.py"],
6368
"transport": "stdio",
6469
},
@@ -84,7 +89,9 @@ def __init__(self, connections: dict[str, Connection] | None = None) -> None:
8489
```
8590
8691
"""
87-
self.connections: dict[str, Connection] = connections if connections is not None else {}
92+
self.connections: dict[str, Connection] = (
93+
connections if connections is not None else {}
94+
)
8895

8996
@asynccontextmanager
9097
async def session(
@@ -107,7 +114,10 @@ async def session(
107114
108115
"""
109116
if server_name not in self.connections:
110-
msg = f"Couldn't find a server with name '{server_name}', expected one of '{list(self.connections.keys())}'"
117+
msg = (
118+
f"Couldn't find a server with name '{server_name}', "
119+
f"expected one of '{list(self.connections.keys())}'"
120+
)
111121
raise ValueError(msg)
112122

113123
async with create_session(self.connections[server_name]) as session:
@@ -130,14 +140,19 @@ async def get_tools(self, *, server_name: str | None = None) -> list[BaseTool]:
130140
"""
131141
if server_name is not None:
132142
if server_name not in self.connections:
133-
msg = f"Couldn't find a server with name '{server_name}', expected one of '{list(self.connections.keys())}'"
143+
msg = (
144+
f"Couldn't find a server with name '{server_name}', "
145+
f"expected one of '{list(self.connections.keys())}'"
146+
)
134147
raise ValueError(msg)
135148
return await load_mcp_tools(None, connection=self.connections[server_name])
136149

137150
all_tools: list[BaseTool] = []
138151
load_mcp_tool_tasks = []
139152
for connection in self.connections.values():
140-
load_mcp_tool_task = asyncio.create_task(load_mcp_tools(None, connection=connection))
153+
load_mcp_tool_task = asyncio.create_task(
154+
load_mcp_tools(None, connection=connection)
155+
)
141156
load_mcp_tool_tasks.append(load_mcp_tool_task)
142157
tools_list = await asyncio.gather(*load_mcp_tool_tasks)
143158
for tools in tools_list:
@@ -165,7 +180,8 @@ async def get_resources(
165180
166181
Args:
167182
server_name: Name of the server to get resources from
168-
uris: Optional resource URI or list of URIs to load. If not provided, all resources will be loaded.
183+
uris: Optional resource URI or list of URIs to load. If not provided,
184+
all resources will be loaded.
169185
170186
Returns:
171187
A list of LangChain Blobs

langchain_mcp_adapters/prompts.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,6 @@ async def load_mcp_prompt(
5353
"""
5454
response = await session.get_prompt(name, arguments)
5555
return [
56-
convert_mcp_prompt_message_to_langchain_message(message) for message in response.messages
56+
convert_mcp_prompt_message_to_langchain_message(message)
57+
for message in response.messages
5758
]

langchain_mcp_adapters/resources.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
from mcp.types import BlobResourceContents, ResourceContents, TextResourceContents
1212

1313

14-
def convert_mcp_resource_to_langchain_blob(resource_uri: str, contents: ResourceContents) -> Blob:
14+
def convert_mcp_resource_to_langchain_blob(
15+
resource_uri: str, contents: ResourceContents
16+
) -> Blob:
1517
"""Convert an MCP resource content to a LangChain Blob.
1618
1719
Args:
@@ -30,7 +32,9 @@ def convert_mcp_resource_to_langchain_blob(resource_uri: str, contents: Resource
3032
msg = f"Unsupported content type for URI {resource_uri}"
3133
raise TypeError(msg)
3234

33-
return Blob.from_data(data=data, mime_type=contents.mimeType, metadata={"uri": resource_uri})
35+
return Blob.from_data(
36+
data=data, mime_type=contents.mimeType, metadata={"uri": resource_uri}
37+
)
3438

3539

3640
async def get_mcp_resource(session: ClientSession, uri: str) -> list[Blob]:
@@ -48,7 +52,8 @@ async def get_mcp_resource(session: ClientSession, uri: str) -> list[Blob]:
4852
return []
4953

5054
return [
51-
convert_mcp_resource_to_langchain_blob(uri, content) for content in contents_result.contents
55+
convert_mcp_resource_to_langchain_blob(uri, content)
56+
for content in contents_result.contents
5257
]
5358

5459

langchain_mcp_adapters/sessions.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class StdioConnection(TypedDict):
9595

9696

9797
class SSEConnection(TypedDict):
98-
"""Configuration for Server-Sent Events (SSE) transport connections to MCP servers."""
98+
"""Configuration for Server-Sent Events (SSE) transport connections to MCP."""
9999

100100
transport: Literal["sse"]
101101

@@ -172,7 +172,9 @@ class WebsocketConnection(TypedDict):
172172
"""Additional keyword arguments to pass to the ClientSession"""
173173

174174

175-
Connection = StdioConnection | SSEConnection | StreamableHttpConnection | WebsocketConnection
175+
Connection = (
176+
StdioConnection | SSEConnection | StreamableHttpConnection | WebsocketConnection
177+
)
176178

177179

178180
@asynccontextmanager
@@ -183,7 +185,9 @@ async def _create_stdio_session( # noqa: PLR0913
183185
env: dict[str, str] | None = None,
184186
cwd: str | Path | None = None,
185187
encoding: str = DEFAULT_ENCODING,
186-
encoding_error_handler: Literal["strict", "ignore", "replace"] = DEFAULT_ENCODING_ERROR_HANDLER,
188+
encoding_error_handler: Literal[
189+
"strict", "ignore", "replace"
190+
] = DEFAULT_ENCODING_ERROR_HANDLER,
187191
session_kwargs: dict[str, Any] | None = None,
188192
) -> AsyncIterator[ClientSession]:
189193
"""Create a new session to an MCP server using stdio.
@@ -201,7 +205,7 @@ async def _create_stdio_session( # noqa: PLR0913
201205
An initialized ClientSession.
202206
"""
203207
# NOTE: execution commands (e.g., `uvx` / `npx`) require PATH envvar to be set.
204-
# To address this, we automatically inject existing PATH envvar into the `env` value,
208+
# To address this, we automatically inject existing PATH envvar into the `env`,
205209
# if it's not already set.
206210
env = env or {}
207211
if "PATH" not in env:
@@ -282,7 +286,8 @@ async def _create_streamable_http_session( # noqa: PLR0913
282286
url: URL of the endpoint to connect to.
283287
headers: HTTP headers to send to the endpoint.
284288
timeout: HTTP timeout.
285-
sse_read_timeout: How long the client will wait for a new event before disconnecting.
289+
sse_read_timeout: How long the client will wait for a new event before
290+
disconnecting.
286291
terminate_on_close: Whether to terminate the session on close.
287292
session_kwargs: Additional keyword arguments to pass to the ClientSession.
288293
httpx_client_factory: Custom factory for httpx.AsyncClient (optional).
@@ -334,7 +339,7 @@ async def _create_websocket_session(
334339
except ImportError:
335340
msg = (
336341
"Could not import websocket_client. "
337-
"To use Websocket connections, please install the required dependency with: "
342+
"To use Websocket connections, please install the required dependency: "
338343
"'pip install mcp[ws]' or 'pip install websockets'"
339344
)
340345
raise ImportError(msg) from None

langchain_mcp_adapters/tools.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66

77
from typing import Any, cast, get_args
88

9-
from langchain_core.tools import BaseTool, InjectedToolArg, StructuredTool, ToolException
9+
from langchain_core.tools import (
10+
BaseTool,
11+
InjectedToolArg,
12+
StructuredTool,
13+
ToolException,
14+
)
1015
from langchain_core.tools.base import get_all_basemodel_annotations
1116
from mcp import ClientSession
1217
from mcp.server.fastmcp.tools import Tool as FastMCPTool
@@ -172,7 +177,8 @@ async def load_mcp_tools(
172177
tools = await _list_all_tools(session)
173178

174179
return [
175-
convert_mcp_tool_to_langchain_tool(session, tool, connection=connection) for tool in tools
180+
convert_mcp_tool_to_langchain_tool(session, tool, connection=connection)
181+
for tool in tools
176182
]
177183

178184

@@ -225,7 +231,9 @@ def to_fastmcp(tool: BaseTool) -> FastMCPTool:
225231
field: (field_info.annotation, field_info)
226232
for field, field_info in tool.tool_call_schema.model_fields.items()
227233
}
228-
arg_model = create_model(f"{tool.name}Arguments", **field_definitions, __base__=ArgModelBase)
234+
arg_model = create_model(
235+
f"{tool.name}Arguments", **field_definitions, __base__=ArgModelBase
236+
)
229237
fn_metadata = FuncMetadata(arg_model=arg_model)
230238

231239
# We'll use an Any type for the function return type.

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,12 @@ asyncio_mode = "auto"
4545
asyncio_default_fixture_loop_scope = "function"
4646

4747
[tool.ruff]
48-
line-length = 100
48+
line-length = 88
4949
target-version = "py310"
5050

5151
[tool.ruff.lint]
5252
select = [ "ALL",]
5353
ignore = [
54-
"E501", # line-length
5554
"COM812", # conflict with formatter
5655
]
5756

tests/conftest.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ def websocket_server(websocket_server_port: int) -> Generator[None, None, None]:
4646
proc.kill()
4747
proc.join(timeout=2)
4848
if proc.is_alive():
49-
raise RuntimeError("Server process is still alive after attempting to terminate it")
49+
raise RuntimeError(
50+
"Server process is still alive after attempting to terminate it"
51+
)
5052

5153

5254
@pytest.fixture
@@ -58,7 +60,9 @@ def socket_enabled():
5860
pytest_socket.enable_socket()
5961
previous_state = pytest_socket.socket_allow_hosts()
6062
# Only allow connections to localhost
61-
pytest_socket.socket_allow_hosts(["127.0.0.1", "localhost"], allow_unix_socket=True)
63+
pytest_socket.socket_allow_hosts(
64+
["127.0.0.1", "localhost"], allow_unix_socket=True
65+
)
6266
yield
6367
finally:
6468
# Restore previous state

tests/servers/math_server.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ def configure_assistant(skills: str) -> list[dict]:
2020
return [
2121
{
2222
"role": "assistant",
23-
"content": f"You are a helpful assistant. You have the following skills: {skills}. Always use only one tool at a time.",
23+
"content": (
24+
f"You are a helpful assistant. You have these skills: {skills}. "
25+
"Always use only one tool at a time."
26+
),
2427
},
2528
]
2629

tests/test_client.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,28 @@ async def test_multi_server_mcp_client(
1515
websocket_server,
1616
websocket_server_port: int,
1717
):
18-
"""Test that the MultiServerMCPClient can connect to multiple servers and load tools."""
18+
"""Test that MultiServerMCPClient can connect to multiple servers and load tools."""
1919
# Get the absolute path to the server scripts
2020
current_dir = Path(__file__).parent
2121
math_server_path = os.path.join(current_dir, "servers/math_server.py")
2222
weather_server_path = os.path.join(current_dir, "servers/weather_server.py")
2323

2424
client = MultiServerMCPClient(
2525
{
26-
"math": {"command": "python", "args": [math_server_path], "transport": "stdio"},
27-
"weather": {"command": "python", "args": [weather_server_path], "transport": "stdio"},
28-
"time": {"url": f"ws://127.0.0.1:{websocket_server_port}/ws", "transport": "websocket"},
26+
"math": {
27+
"command": "python",
28+
"args": [math_server_path],
29+
"transport": "stdio",
30+
},
31+
"weather": {
32+
"command": "python",
33+
"args": [weather_server_path],
34+
"transport": "stdio",
35+
},
36+
"time": {
37+
"url": f"ws://127.0.0.1:{websocket_server_port}/ws",
38+
"transport": "websocket",
39+
},
2940
},
3041
)
3142
# Check that we have tools from both servers
@@ -93,8 +104,15 @@ async def test_multi_server_connect_methods(
93104
# Initialize client without initial connections
94105
client = MultiServerMCPClient(
95106
{
96-
"math": {"command": "python", "args": [math_server_path], "transport": "stdio"},
97-
"time": {"url": f"ws://127.0.0.1:{websocket_server_port}/ws", "transport": "websocket"},
107+
"math": {
108+
"command": "python",
109+
"args": [math_server_path],
110+
"transport": "stdio",
111+
},
112+
"time": {
113+
"url": f"ws://127.0.0.1:{websocket_server_port}/ws",
114+
"transport": "websocket",
115+
},
98116
},
99117
)
100118
tool_names = set()
@@ -127,7 +145,13 @@ async def test_get_prompt():
127145
math_server_path = os.path.join(current_dir, "servers/math_server.py")
128146

129147
client = MultiServerMCPClient(
130-
{"math": {"command": "python", "args": [math_server_path], "transport": "stdio"}},
148+
{
149+
"math": {
150+
"command": "python",
151+
"args": [math_server_path],
152+
"transport": "stdio",
153+
}
154+
},
131155
)
132156
# Test getting a prompt from the math server
133157
messages = await client.get_prompt(

0 commit comments

Comments
 (0)