Skip to content

Commit c7455a8

Browse files
authored
Merge branch 'main' into mcp-restructure
2 parents 09866c4 + f740ceb commit c7455a8

File tree

23 files changed

+453
-120
lines changed

23 files changed

+453
-120
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@ venv
1111
.python-version
1212
**.egg-info/
1313
__pycache__/**
14-
14+
.coverage
15+
**/.coverage
16+
build/
17+
**/build/

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"packages/toolbox-langchain":"0.5.0","packages/toolbox-core":"0.5.0","packages/toolbox-llamaindex":"0.5.0"}
1+
{"packages/toolbox-langchain":"0.5.2","packages/toolbox-core":"0.5.2","packages/toolbox-llamaindex":"0.5.2"}

packages/toolbox-core/CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,31 @@
11
# Changelog
22

3+
## [0.5.2](https://github.com/googleapis/mcp-toolbox-sdk-python/compare/toolbox-core-v0.5.1...toolbox-core-v0.5.2) (2025-09-22)
4+
5+
6+
### Miscellaneous Chores
7+
8+
* **deps:** update python-nonmajor ([#372](https://github.com/googleapis/mcp-toolbox-sdk-python/issues/372)) ([d915624](https://github.com/googleapis/mcp-toolbox-sdk-python/commit/d9156246fd35b7813c49ff4b4bc01cf26b3de9f9))
9+
10+
## [0.5.1](https://github.com/googleapis/mcp-toolbox-sdk-python/compare/toolbox-core-v0.5.0...toolbox-core-v0.5.1) (2025-09-17)
11+
12+
13+
### Bug Fixes
14+
15+
* **toolbox-core:** Use typing.Annotated for reliable parameter descriptions instead of docstrings ([#371](https://github.com/googleapis/mcp-toolbox-sdk-python/issues/371)) ([eb76680](https://github.com/googleapis/mcp-toolbox-sdk-python/commit/eb76680d24c344241f3c15293cd12904bbf91b0d))
16+
17+
18+
### Documentation
19+
20+
* Update langgraph sample in toolbox-core ([#366](https://github.com/googleapis/mcp-toolbox-sdk-python/issues/366)) ([fe35082](https://github.com/googleapis/mcp-toolbox-sdk-python/commit/fe35082104d8039986eccbdab937f5f3e8b2042b))
21+
22+
23+
### Miscellaneous Chores
24+
25+
* Remove redundant test for test_add_auth_token_getter_unused_token ([#347](https://github.com/googleapis/mcp-toolbox-sdk-python/issues/347)) ([dccaf1b](https://github.com/googleapis/mcp-toolbox-sdk-python/commit/dccaf1bd70e4e788fd80a9ef952aede4549b2fc7))
26+
* Remove duplicate header check during initialization ([#357](https://github.com/googleapis/mcp-toolbox-sdk-python/issues/357)) ([888170b](https://github.com/googleapis/mcp-toolbox-sdk-python/commit/888170b3c34744089fb3b6f2b6f613c4cd718a89))
27+
28+
329
## [0.5.0](https://github.com/googleapis/mcp-toolbox-sdk-python/compare/toolbox-core-v0.4.0...toolbox-core-v0.5.0) (2025-08-19)
430

531

packages/toolbox-core/README.md

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -232,39 +232,67 @@ Here's a conceptual example adapting the [official LangGraph tool calling
232232
guide](https://langchain-ai.github.io/langgraph/how-tos/tool-calling):
233233

234234
```py
235+
import asyncio
236+
from typing import Annotated
237+
from typing_extensions import TypedDict
238+
from langchain_core.messages import HumanMessage, BaseMessage
235239
from toolbox_core import ToolboxClient
236240
from langchain_google_vertexai import ChatVertexAI
237-
from langgraph.graph import StateGraph, MessagesState, START, END
241+
from langgraph.graph import StateGraph, START, END
238242
from langgraph.prebuilt import ToolNode
239243
from langchain.tools import StructuredTool
244+
from langgraph.graph.message import add_messages
240245

241-
async with ToolboxClient("http://127.0.0.1:5000") as toolbox:
242-
tools = await toolbox.load_toolset()
243-
wrapped_tools = [StructuredTool.from_function(tool, parse_docstring=True) for tool in tools]
244-
model_with_tools = ChatVertexAI(model="gemini-2.0-flash-001").bind_tools(wrapped_tools)
246+
class State(TypedDict):
247+
messages: Annotated[list[BaseMessage], add_messages]
248+
249+
async def main():
250+
async with ToolboxClient("http://127.0.0.1:5000") as toolbox:
251+
tools = await toolbox.load_toolset()
252+
wrapped_tools = [StructuredTool.from_function(tool, parse_docstring=True) for tool in tools]
253+
model_with_tools = ChatVertexAI(model="gemini-2.5-flash").bind_tools(wrapped_tools)
254+
tool_node = ToolNode(wrapped_tools)
255+
256+
def call_agent(state: State):
257+
response = model_with_tools.invoke(state["messages"])
258+
return {"messages": [response]}
259+
260+
def should_continue(state: State):
261+
last_message = state["messages"][-1]
262+
if last_message.tool_calls:
263+
return "tools"
264+
return END
265+
266+
graph_builder = StateGraph(State)
267+
268+
graph_builder.add_node("agent", call_agent)
269+
graph_builder.add_node("tools", tool_node)
270+
271+
graph_builder.add_edge(START, "agent")
272+
graph_builder.add_conditional_edges(
273+
"agent",
274+
should_continue,
275+
)
276+
graph_builder.add_edge("tools", "agent")
245277

246-
def call_model(state: MessagesState):
247-
messages = state["messages"]
248-
response = model_with_tools.invoke(messages)
249-
return {"messages": [response]}
278+
app = graph_builder.compile()
250279

251-
def should_continue(state: MessagesState):
252-
messages = state["messages"]
253-
last_message = messages[-1]
254-
if last_message.tool_calls:
255-
return "tools"
256-
return END
280+
prompt = "What is the weather in London?"
281+
inputs = {"messages": [HumanMessage(content=prompt)]}
257282

258-
workflow = StateGraph(MessagesState)
283+
print(f"User: {prompt}\n")
284+
print("--- Streaming Agent Steps ---")
259285

260-
workflow.add_node("agent", call_model)
261-
workflow.add_node("tools", ToolNode(wrapped_tools))
286+
events = app.stream(
287+
inputs,
288+
stream_mode="values",
289+
)
262290

263-
workflow.add_edge(START, "agent")
264-
workflow.add_conditional_edges("agent", should_continue, ["tools", END])
265-
workflow.add_edge("tools", "agent")
291+
for event in events:
292+
event["messages"][-1].pretty_print()
293+
print("\n---\n")
266294

267-
app = workflow.compile()
295+
asyncio.run(main())
268296
```
269297

270298
## Client to Server Authentication

packages/toolbox-core/integration.cloudbuild.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@ steps:
2626
name: 'python:${_VERSION}'
2727
args:
2828
- install
29+
- '-e'
2930
- 'packages/toolbox-core[test]'
3031
- '--user'
3132
entrypoint: pip
3233
- id: Run integration tests
3334
name: 'python:${_VERSION}'
35+
dir: 'packages/toolbox-core'
3436
env:
3537
- TOOLBOX_URL=$_TOOLBOX_URL
3638
- TOOLBOX_VERSION=$_TOOLBOX_VERSION
@@ -39,11 +41,11 @@ steps:
3941
args:
4042
- '-c'
4143
- >-
42-
python -m pytest packages/toolbox-core/tests/
44+
python -m pytest --cov=src/toolbox_core --cov-report=term --cov-fail-under=90 tests/
4345
entrypoint: /bin/bash
4446
options:
4547
logging: CLOUD_LOGGING_ONLY
4648
substitutions:
4749
_VERSION: '3.13'
48-
_TOOLBOX_VERSION: '0.12.0'
50+
_TOOLBOX_VERSION: '0.16.0'
4951
_TOOLBOX_MANIFEST_VERSION: '34'

packages/toolbox-core/pyproject.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ Changelog = "https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/pack
4444

4545
[project.optional-dependencies]
4646
test = [
47-
"black[jupyter]==25.1.0",
48-
"isort==6.0.1",
49-
"mypy==1.17.1",
47+
"black[jupyter]==25.9.0",
48+
"isort==6.1.0",
49+
"mypy==1.18.2",
5050
"pytest==8.4.2",
5151
"pytest-aioresponses==0.3.0",
52-
"pytest-asyncio==1.1.0",
53-
"pytest-cov==6.3.0",
54-
"pytest-mock==3.15.0",
52+
"pytest-asyncio==1.2.0",
53+
"pytest-cov==7.0.0",
54+
"pytest-mock==3.15.1",
5555
"google-cloud-secret-manager==2.24.0",
56-
"google-cloud-storage==3.3.1",
56+
"google-cloud-storage==3.4.0",
5757
"aioresponses==0.7.8"
5858
]
5959
[build-system]
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
aiohttp==3.12.15
2-
pydantic==2.11.7
1+
aiohttp==3.13.0
2+
pydantic==2.11.10
33
deprecated==1.2.18
44
requests==2.32.5
5-
google-auth==2.40.3
5+
google-auth==2.41.1

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

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,6 @@ def __init__(
104104
self.__annotations__ = {p.name: p.annotation for p in inspect_type_params}
105105
self.__qualname__ = f"{self.__class__.__qualname__}.{self.__name__}"
106106

107-
# Validate conflicting Headers/Auth Tokens
108-
request_header_names = client_headers.keys()
109-
auth_token_names = [
110-
self.__get_auth_header(auth_token_name)
111-
for auth_token_name in auth_service_token_getters.keys()
112-
]
113-
duplicates = request_header_names & auth_token_names
114-
if duplicates:
115-
raise ValueError(
116-
f"Client header(s) `{', '.join(duplicates)}` already registered in client. "
117-
f"Cannot register client the same headers in the client as well as tool."
118-
)
119-
120107
# map of parameter name to auth service required by it
121108
self.__required_authn_params = required_authn_params
122109
# sequence of authorization tokens required by it

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
__version__ = "0.5.0"
15+
__version__ = "0.5.2"

packages/toolbox-core/tests/test_client.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,35 +1478,3 @@ async def test_add_headers_duplicate_fail(self, static_header):
14781478
match=f"Client header\\(s\\) `X-Static-Header` already registered",
14791479
):
14801480
client.add_headers(static_header)
1481-
1482-
@pytest.mark.asyncio
1483-
async def test_client_header_auth_token_conflict_fail(
1484-
self, aioresponses, test_tool_auth
1485-
):
1486-
"""
1487-
Tests that loading a tool fails if a client header conflicts with an
1488-
auth token name.
1489-
"""
1490-
tool_name = "auth_conflict_tool"
1491-
conflict_key = "my-auth-service_token"
1492-
manifest = ManifestSchema(
1493-
serverVersion="0.0.0", tools={tool_name: test_tool_auth}
1494-
)
1495-
1496-
conflicting_headers = {conflict_key: "some_value"}
1497-
auth_getters = {"my-auth-service": lambda: "token_val"}
1498-
1499-
aioresponses.get(
1500-
f"{TEST_BASE_URL}/api/tool/{tool_name}",
1501-
payload=manifest.model_dump(),
1502-
status=200,
1503-
)
1504-
1505-
async with ToolboxClient(
1506-
TEST_BASE_URL, client_headers=conflicting_headers
1507-
) as client:
1508-
with pytest.raises(
1509-
ValueError,
1510-
match=f"Client header\\(s\\) `{conflict_key}` already registered",
1511-
):
1512-
await client.load_tool(tool_name, auth_token_getters=auth_getters)

0 commit comments

Comments
 (0)