Skip to content

Commit 08baabf

Browse files
authored
Merge branch 'main' into fix-auth
2 parents 726de7e + a5897c8 commit 08baabf

File tree

14 files changed

+250
-92
lines changed

14 files changed

+250
-92
lines changed

packages/toolbox-core/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Changelog = "https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/pack
4444
test = [
4545
"black[jupyter]==25.1.0",
4646
"isort==6.0.1",
47-
"mypy==1.16.1",
47+
"mypy==1.17.0",
4848
"pytest==8.4.1",
4949
"pytest-aioresponses==0.3.0",
5050
"pytest-asyncio==1.0.0",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
aiohttp==3.12.13
1+
aiohttp==3.12.14
22
pydantic==2.11.7
33
deprecated==1.2.18

packages/toolbox-core/src/toolbox_core/auth_methods.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@
2626
auth_methods.aget_google_id_token,
2727
"https://toolbox-service-url"
2828
)
29-
client = ToolboxClient(URL, client_headers={"Authorization": auth_token_provider})
30-
await client.make_request()
29+
async with ToolboxClient(
30+
URL,
31+
client_headers={"Authorization": auth_token_provider},
32+
) as toolbox:
33+
tools = await toolbox.load_toolset()
3134
"""
3235

3336
from datetime import datetime, timedelta, timezone

packages/toolbox-core/src/toolbox_core/sync_client.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,22 @@ def add_headers(
176176
self.__async_client.add_headers(headers)
177177

178178
def __enter__(self):
179-
"""Enter the runtime context related to this client instance."""
179+
"""
180+
Enter the runtime context related to this client instance.
181+
182+
Allows the client to be used as a context manager
183+
(e.g., `with ToolboxSyncClient(...) as client:`).
184+
185+
Returns:
186+
self: The client instance itself.
187+
"""
180188
return self
181189

182190
def __exit__(self, exc_type, exc_val, exc_tb):
183-
"""Exit the runtime context and close the client session."""
191+
"""
192+
Exit the runtime context and close the internally managed session.
193+
194+
Allows the client to be used as a context manager
195+
(e.g., `with ToolboxSyncClient(...) as client:`).
196+
"""
184197
self.close()

packages/toolbox-langchain/README.md

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,20 @@ from toolbox_langchain import ToolboxClient
5757
from langchain_google_vertexai import ChatVertexAI
5858
from langgraph.prebuilt import create_react_agent
5959

60-
toolbox = ToolboxClient("http://127.0.0.1:5000")
61-
tools = toolbox.load_toolset()
60+
async with ToolboxClient("http://127.0.0.1:5000") as toolbox:
61+
tools = toolbox.load_toolset()
6262

63-
model = ChatVertexAI(model="gemini-2.0-flash-001")
64-
agent = create_react_agent(model, tools)
63+
model = ChatVertexAI(model="gemini-2.0-flash-001")
64+
agent = create_react_agent(model, tools)
6565

66-
prompt = "How's the weather today?"
66+
prompt = "How's the weather today?"
6767

68-
for s in agent.stream({"messages": [("user", prompt)]}, stream_mode="values"):
69-
message = s["messages"][-1]
70-
if isinstance(message, tuple):
71-
print(message)
72-
else:
73-
message.pretty_print()
68+
for s in agent.stream({"messages": [("user", prompt)]}, stream_mode="values"):
69+
message = s["messages"][-1]
70+
if isinstance(message, tuple):
71+
print(message)
72+
else:
73+
message.pretty_print()
7474
```
7575

7676
> [!TIP]
@@ -86,7 +86,7 @@ Import and initialize the toolbox client.
8686
from toolbox_langchain import ToolboxClient
8787

8888
# Replace with your Toolbox service's URL
89-
toolbox = ToolboxClient("http://127.0.0.1:5000")
89+
async with ToolboxClient("http://127.0.0.1:5000") as toolbox:
9090
```
9191

9292
## Loading Tools
@@ -241,10 +241,10 @@ You can configure these dynamic headers as follows:
241241
```python
242242
from toolbox_langchain import ToolboxClient
243243

244-
client = ToolboxClient(
244+
async with ToolboxClient(
245245
"toolbox-url",
246246
client_headers={"header1": header1_getter, "header2": header2_getter, ...}
247-
)
247+
) as client:
248248
```
249249

250250
### Authenticating with Google Cloud Servers
@@ -273,13 +273,13 @@ For Toolbox servers hosted on Google Cloud (e.g., Cloud Run) and requiring
273273
from toolbox_core import auth_methods
274274

275275
auth_token_provider = auth_methods.aget_google_id_token # can also use sync method
276-
client = ToolboxClient(
276+
async with ToolboxClient(
277277
URL,
278278
client_headers={"Authorization": auth_token_provider},
279-
)
280-
tools = client.load_toolset()
279+
) as client:
280+
tools = client.load_toolset()
281281

282-
# Now, you can use the client as usual.
282+
# Now, you can use the client as usual.
283283
```
284284

285285

@@ -319,16 +319,16 @@ async def get_auth_token():
319319
#### Add Authentication to a Tool
320320

321321
```py
322-
toolbox = ToolboxClient("http://127.0.0.1:5000")
323-
tools = toolbox.load_toolset()
322+
async with ToolboxClient("http://127.0.0.1:5000") as toolbox:
323+
tools = toolbox.load_toolset()
324324

325-
auth_tool = tools[0].add_auth_token_getter("my_auth", get_auth_token) # Single token
325+
auth_tool = tools[0].add_auth_token_getter("my_auth", get_auth_token) # Single token
326326

327-
multi_auth_tool = tools[0].add_auth_token_getters({"auth_1": get_auth_1}, {"auth_2": get_auth_2}) # Multiple tokens
327+
multi_auth_tool = tools[0].add_auth_token_getters({"auth_1": get_auth_1}, {"auth_2": get_auth_2}) # Multiple tokens
328328

329-
# OR
329+
# OR
330330

331-
auth_tools = [tool.add_auth_token_getter("my_auth", get_auth_token) for tool in tools]
331+
auth_tools = [tool.add_auth_token_getter("my_auth", get_auth_token) for tool in tools]
332332
```
333333

334334
#### Add Authentication While Loading
@@ -354,12 +354,12 @@ async def get_auth_token():
354354
# This example just returns a placeholder. Replace with your actual token retrieval.
355355
return "YOUR_ID_TOKEN" # Placeholder
356356

357-
toolbox = ToolboxClient("http://127.0.0.1:5000")
358-
tool = toolbox.load_tool("my-tool")
357+
async with ToolboxClient("http://127.0.0.1:5000") as toolbox:
358+
tool = toolbox.load_tool("my-tool")
359359

360-
auth_tool = tool.add_auth_token_getter("my_auth", get_auth_token)
361-
result = auth_tool.invoke({"input": "some input"})
362-
print(result)
360+
auth_tool = tool.add_auth_token_getter("my_auth", get_auth_token)
361+
result = auth_tool.invoke({"input": "some input"})
362+
print(result)
363363
```
364364

365365
## Binding Parameter Values
@@ -374,16 +374,16 @@ modified by the LLM. This is useful for:
374374
### Binding Parameters to a Tool
375375

376376
```py
377-
toolbox = ToolboxClient("http://127.0.0.1:5000")
378-
tools = toolbox.load_toolset()
377+
async with ToolboxClient("http://127.0.0.1:5000") as toolbox:
378+
tools = toolbox.load_toolset()
379379

380-
bound_tool = tool[0].bind_param("param", "value") # Single param
380+
bound_tool = tool[0].bind_param("param", "value") # Single param
381381

382-
multi_bound_tool = tools[0].bind_params({"param1": "value1", "param2": "value2"}) # Multiple params
382+
multi_bound_tool = tools[0].bind_params({"param1": "value1", "param2": "value2"}) # Multiple params
383383

384-
# OR
384+
# OR
385385

386-
bound_tools = [tool.bind_param("param", "value") for tool in tools]
386+
bound_tools = [tool.bind_param("param", "value") for tool in tools]
387387
```
388388

389389
### Binding Parameters While Loading
@@ -429,10 +429,10 @@ import asyncio
429429
from toolbox_langchain import ToolboxClient
430430

431431
async def main():
432-
toolbox = ToolboxClient("http://127.0.0.1:5000")
433-
tool = await client.aload_tool("my-tool")
434-
tools = await client.aload_toolset()
435-
response = await tool.ainvoke()
432+
async with ToolboxClient("http://127.0.0.1:5000") as toolbox:
433+
tool = await client.aload_tool("my-tool")
434+
tools = await client.aload_toolset()
435+
response = await tool.ainvoke()
436436

437437
if __name__ == "__main__":
438438
asyncio.run(main())

packages/toolbox-langchain/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Changelog = "https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/pack
4646
test = [
4747
"black[jupyter]==25.1.0",
4848
"isort==6.0.1",
49-
"mypy==1.16.1",
49+
"mypy==1.17.0",
5050
"pytest-asyncio==1.0.0",
5151
"pytest==8.4.1",
5252
"pytest-cov==6.2.1",

packages/toolbox-langchain/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
langchain-core==0.3.68
33
PyYAML==6.0.2
44
pydantic==2.11.7
5-
aiohttp==3.12.13
5+
aiohttp==3.12.14
66
deprecated==1.2.18

packages/toolbox-langchain/src/toolbox_langchain/async_client.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,28 @@ def load_toolset(
190190
strict: bool = False,
191191
) -> list[AsyncToolboxTool]:
192192
raise NotImplementedError("Synchronous methods not supported by async client.")
193+
194+
async def close(self):
195+
"""Close the underlying synchronous client."""
196+
await self.__core_client.close()
197+
198+
async def __aenter__(self):
199+
"""
200+
Enter the runtime context related to this client instance.
201+
202+
Allows the client to be used as an asynchronous context manager
203+
(e.g., `async with AsyncToolboxClient(...) as client:`).
204+
205+
Returns:
206+
self: The client instance itself.
207+
"""
208+
return self
209+
210+
async def __aexit__(self, exc_type, exc_val, exc_tb):
211+
"""
212+
Exit the runtime context and close the internally managed session.
213+
214+
Allows the client to be used as an asynchronous context manager
215+
(e.g., `async with AsyncToolboxClient(...) as client:`).
216+
"""
217+
await self.close()

packages/toolbox-langchain/src/toolbox_langchain/client.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,3 +291,49 @@ def load_toolset(
291291
for core_sync_tool in core_sync_tools:
292292
tools.append(ToolboxTool(core_tool=core_sync_tool))
293293
return tools
294+
295+
def close(self):
296+
"""Close the underlying synchronous client."""
297+
self.__core_client.close()
298+
299+
async def __aenter__(self):
300+
"""
301+
Enter the runtime context related to this client instance.
302+
303+
Allows the client to be used as an asynchronous context manager
304+
(e.g., `async with ToolboxClient(...) as client:`).
305+
306+
Returns:
307+
self: The client instance itself.
308+
"""
309+
return self
310+
311+
async def __aexit__(self, exc_type, exc_val, exc_tb):
312+
"""
313+
Exit the runtime context and close the internally managed session.
314+
315+
Allows the client to be used as an asynchronous context manager
316+
(e.g., `async with ToolboxClient(...) as client:`).
317+
"""
318+
self.close()
319+
320+
def __enter__(self):
321+
"""
322+
Enter the runtime context related to this client instance.
323+
324+
Allows the client to be used as a context manager
325+
(e.g., `with ToolboxClient(...) as client:`).
326+
327+
Returns:
328+
self: The client instance itself.
329+
"""
330+
return self
331+
332+
def __exit__(self, exc_type, exc_val, exc_tb):
333+
"""
334+
Exit the runtime context and close the internally managed session.
335+
336+
Allows the client to be used as a context manager
337+
(e.g., `with ToolboxClient(...) as client:`).
338+
"""
339+
self.close()

0 commit comments

Comments
 (0)