From 8a152aa64260e5920b4a32e686d51f0c65548fa7 Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Wed, 9 Apr 2025 16:49:05 +0530 Subject: [PATCH 01/14] doc: Add a repo-level README --- README.md | 489 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 254 insertions(+), 235 deletions(-) diff --git a/README.md b/README.md index 8d011a01..56a695da 100644 --- a/README.md +++ b/README.md @@ -1,342 +1,361 @@ -![MCP Toolbox Logo](https://raw.githubusercontent.com/googleapis/genai-toolbox/main/logo.png) -# MCP Toolbox LangChain SDK +![MCP Toolbox +Logo](https://raw.githubusercontent.com/googleapis/genai-toolbox/main/logo.png) +# MCP Toolbox SDKs for Python -This SDK allows you to seamlessly integrate the functionalities of -[Toolbox](https://github.com/googleapis/genai-toolbox) into your LangChain LLM -applications, enabling advanced orchestration and interaction with GenAI models. +[![License: Apache +2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![PyPI - Python +Version](https://img.shields.io/pypi/pyversions/toolbox-core)](https://pypi.org/project/toolbox-core/) + +This repository contains Python SDKs designed to seamlessly integrate the +functionalities of the [MCP +Toolbox](https://github.com/googleapis/genai-toolbox) into your Gen AI +applications. These SDKs allow you to load tools defined in Toolbox and use them +as standard Python functions or objects within popular orchestration frameworks +or your custom code. + +This simplifies the process of incorporating external functionalities (like +Databases or APIs) managed by Toolbox into your GenAI applications. - -## Table of Contents -- [Installation](#installation) +- [Overview](#overview) +- [Available Packages](#available-packages) + - [toolbox-core](#toolbox-core) + - [toolbox-langchain](#toolbox-langchain) - [Quickstart](#quickstart) -- [Usage](#usage) -- [Loading Tools](#loading-tools) - - [Load a toolset](#load-a-toolset) - - [Load a single tool](#load-a-single-tool) -- [Use with LangChain](#use-with-langchain) -- [Use with LangGraph](#use-with-langgraph) - - [Represent Tools as Nodes](#represent-tools-as-nodes) - - [Connect Tools with LLM](#connect-tools-with-llm) -- [Manual usage](#manual-usage) -- [Authenticating Tools](#authenticating-tools) - - [Supported Authentication Mechanisms](#supported-authentication-mechanisms) - - [Configure Tools](#configure-tools) - - [Configure SDK](#configure-sdk) - - [Add Authentication to a Tool](#add-authentication-to-a-tool) - - [Add Authentication While Loading](#add-authentication-while-loading) - - [Complete Example](#complete-example) -- [Binding Parameter Values](#binding-parameter-values) - - [Binding Parameters to a Tool](#binding-parameters-to-a-tool) - - [Binding Parameters While Loading](#binding-parameters-while-loading) - - [Binding Dynamic Values](#binding-dynamic-values) -- [Asynchronous Usage](#asynchronous-usage) +- [Core Concepts](#core-concepts) + - [Connecting to Toolbox](#connecting-to-toolbox) + - [Loading Tools](#loading-tools) + - [Invoking Tools](#invoking-tools) + - [Synchronous vs. Asynchronous Usage](#synchronous-vs-asynchronous-usage) + - [Authenticating Tools](#authenticating-tools) + - [When is Authentication Needed?](#when-is-authentication-needed) + - [Supported Authentication + Mechanisms](#supported-authentication-mechanisms) + - [SDK Configuration](#sdk-configuration) + - [Binding Parameter Values](#binding-parameter-values) + - [Why Bind Parameters?](#why-bind-parameters) + - [SDK Configuration](#sdk-configuration) +- [Framework-Specific Usage](#framework-specific-usage) + - [Using toolbox-core](#using-toolbox-core) + - [Using toolbox-langchain](#using-toolbox-langchain) +- [Contributing](#contributing) +- [License](#license) +- [Support](#support) -## Installation +## Overview + +The Toolbox service provides a centralized way to manage and expose tools for +use by LLMs. These SDKs act as clients for that service, abstracting away the +API calls needed to fetch tool definitions and invoke them. + +## Available Packages + +This repository hosts the following Python packages: + +### `toolbox-core` + +[![PyPI +version](https://badge.fury.io/py/toolbox-core.svg)](https://badge.fury.io/py/toolbox-core) + +* **Path:** `packages/toolbox-core/` +* **Description:** A framework-agnostic SDK. Provides core functionality + (`ToolboxClient` and `ToolboxTool`) to load and invoke tools. Can be used + directly, with custom frameworks. +* **Details:** [See `toolbox-core` + README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/README.md) + +### `toolbox-langchain` + +[![PyPI +version](https://badge.fury.io/py/toolbox-langchain.svg)](https://badge.fury.io/py/toolbox-langchain) + +* **Path:** `packages/toolbox-langchain/` +* **Description:** Integrates Toolbox tools seamlessly with the + [LangChain](https://python.langchain.com/) ecosystem. Loaded tools are + compatible with LangGraph agents. +* **Details:** [See `toolbox-langchain` + README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain/README.md) + +Install the desired package(s) using pip: ```bash +# For the core, framework-agnostic SDK +pip install toolbox-core + +# For LangChain/LangGraph integration pip install toolbox-langchain ``` ## Quickstart -Here's a minimal example to get you started using -[LangGraph](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.chat_agent_executor.create_react_agent): +To get started using Toolbox tools with an application, follow these general steps: -```py -from toolbox_langchain import ToolboxClient -from langchain_google_vertexai import ChatVertexAI -from langgraph.prebuilt import create_react_agent +1. **Configure and Run the Toolbox Service:** -toolbox = ToolboxClient("http://127.0.0.1:5000") -tools = toolbox.load_toolset() + For detailed instructions on setting up and running the Toolbox service + itself, see: [**Toolbox Getting Started + Guide**](https://github.com/googleapis/genai-toolbox?tab=readme-ov-file#getting-started) -model = ChatVertexAI(model="gemini-1.5-pro-002") -agent = create_react_agent(model, tools) +2. **Install the Toolbox SDK:** -prompt = "How's the weather today?" + Install the appropriate Python SDK package: -for s in agent.stream({"messages": [("user", prompt)]}, stream_mode="values"): - message = s["messages"][-1] - if isinstance(message, tuple): - print(message) - else: - message.pretty_print() -``` + ```bash + pip install toolbox-core + # pip install toolbox-langchain + ``` -## Usage +3. **Load Tools Using the SDK Client:** -Import and initialize the toolbox client. + Once the service is running and the SDK is installed, use the + `ToolboxClient` in your Python code to connect to the service and load the + tools. -```py -from toolbox_langchain import ToolboxClient + ```py + from toolbox_core import ToolboxClient + # from toolbox_langchain import ToolboxClient -# Replace with your Toolbox service's URL -toolbox = ToolboxClient("http://127.0.0.1:5000") -``` + client = ToolboxClient("http://127.0.0.1:5000") -## Loading Tools + tools = await client.load_toolset("toolset_name") + # tools = await client.aload_toolset("toolset_name") + ``` -### Load a toolset +> [!TIP] +> For a complete, step-by-step walkthrough, please refer to the full tutorial: +> [**Toolbox Quickstart +> Tutorial**](https://googleapis.github.io/genai-toolbox/getting-started/local_quickstart) -A toolset is a collection of related tools. You can load all tools in a toolset -or a specific one: +## Core Concepts -```py -# Load all tools -tools = toolbox.load_toolset() +The following concepts apply generally across the different SDK packages, +although specific method names or object types might vary slightly. Refer to the +individual package READMEs for precise details. -# Load a specific toolset -tools = toolbox.load_toolset("my-toolset") -``` +### Connecting to Toolbox -### Load a single tool +Initialize a client, pointing it to the URL where your Toolbox service is +running. ```py -tool = toolbox.load_tool("my-tool") -``` +from toolbox_core import ToolboxClient -Loading individual tools gives you finer-grained control over which tools are -available to your LLM agent. +# replace with your Toolbox service's URL +client = ToolboxClient("http://127.0.0.1:5000") +``` -## Use with LangChain +### Loading Tools -LangChain's agents can dynamically choose and execute tools based on the user -input. Include tools loaded from the Toolbox SDK in the agent's toolkit: +Fetch tool definitions from the Toolbox service. You can load individual tools +by name or load all tools within a specific toolset (or all available toolsets). ```py -from langchain_google_vertexai import ChatVertexAI - -model = ChatVertexAI(model="gemini-1.5-pro-002") +# Load a single tool +tool = await client.load_tool("my-tool") -# Initialize agent with tools -agent = model.bind_tools(tools) +# Load all tools in a specific toolset +tools = await client.load_toolset("my-toolset") -# Run the agent -result = agent.invoke("Do something with the tools") +# Load all tools from all toolsets +all_tools = await client.load_toolset() ``` -## Use with LangGraph +### Invoking Tools -Integrate the Toolbox SDK with LangGraph to use Toolbox service tools within a -graph-based workflow. Follow the [official -guide](https://langchain-ai.github.io/langgraph/) with minimal changes. +Loaded tools behave like callable Python objects or functions. -### Represent Tools as Nodes - -Represent each tool as a LangGraph node, encapsulating the tool's execution within the node's functionality: + * **`toolbox-core`**: Async tools are `awaitable`, sync tools are called + directly. + * **`toolbox-langchain`**: Tools conform to LangChain's `BaseTool` interface + and are typically invoked via `.invoke()` or `.ainvoke()`, often managed by + a LangGraph agent. ```py -from toolbox_langchain import ToolboxClient -from langgraph.graph import StateGraph, MessagesState -from langgraph.prebuilt import ToolNode - -# Define the function that calls the model -def call_model(state: MessagesState): - messages = state['messages'] - response = model.invoke(messages) - return {"messages": [response]} # Return a list to add to existing messages - -model = ChatVertexAI(model="gemini-1.5-pro-002") -builder = StateGraph(MessagesState) -tool_node = ToolNode(tools) - -builder.add_node("agent", call_model) -builder.add_node("tools", tool_node) +# toolbox-core +result = await tool(param1="value1", param2="value2") + +# toolbox-langchain +result = await tool.ainvoke({"param1": "value1", "param2": "value2"}) ``` -### Connect Tools with LLM +### Synchronous vs. Asynchronous Usage -Connect tool nodes with LLM nodes. The LLM decides which tool to use based on -input or context. Tool output can be fed back into the LLM: + * **Asynchronous (Recommended for I/O):** Most SDKs prioritize asynchronous + operations (`async`/`await`) for better performance in I/O-bound tasks like + network calls to the Toolbox service. This requires running your code within + an async event loop (e.g., using `asyncio.run()`). The default + `toolbox-core` `ToolboxClient` is async. The `toolbox-langchain` package + provides async methods like `aload_tool`, `aload_toolset`, `ainvoke`. + * **Synchronous:** For simpler scripts or applications not built around + asyncio, synchronous alternatives are provided. `toolbox-core` offers + `ToolboxSyncClient` and `ToolboxSyncTool`. `toolbox-langchain` provides + synchronous methods like `load_tool`, `load_toolset`, and `invoke`. ```py -from typing import Literal -from langgraph.graph import END, START -from langchain_core.messages import HumanMessage +from toolbox_core import ToolboxSyncClient -# Define the function that determines whether to continue or not -def should_continue(state: MessagesState) -> Literal["tools", END]: - messages = state['messages'] - last_message = messages[-1] - if last_message.tool_calls: - return "tools" # Route to "tools" node if LLM makes a tool call - return END # Otherwise, stop +# replace with your Toolbox service's URL +sync_client = ToolboxSyncClient("http://127.0.0.1:5000") -builder.add_edge(START, "agent") -builder.add_conditional_edges("agent", should_continue) -builder.add_edge("tools", 'agent') +# Load a single tool +tool = sync_client.load_tool("my-tool") -graph = builder.compile() +# Load all tools in a specific toolset +tools = sync_client.load_toolset("my-toolset") -graph.invoke({"messages": [HumanMessage(content="Do something with the tools")]}) +# Load all tools from all toolsets +all_tools = sync_client.load_toolset() ``` -## Manual usage +### Authenticating Tools -Execute a tool manually using the `invoke` method: +Tools configured in the Toolbox service to require authentication need +credentials provided by the SDK during invocation. -```py -result = tools[0].invoke({"name": "Alice", "age": 30}) -``` - -This is useful for testing tools or when you need precise control over tool -execution outside of an agent framework. - -## Authenticating Tools +#### When is Authentication Needed? -> [!WARNING] -> Always use HTTPS to connect your application with the Toolbox service, -> especially when using tools with authentication configured. Using HTTP exposes -> your application to serious security risks. +Authentication is configured *per-tool* within the Toolbox service. If a tool +definition specifies it requires authentication (e.g., an "authenticated +parameter"), the SDK must be configured to provide the necessary token. -Some tools require user authentication to access sensitive data. +#### Supported Authentication Mechanisms -### Supported Authentication Mechanisms -Toolbox currently supports authentication using the [OIDC -protocol](https://openid.net/specs/openid-connect-core-1_0.html) with [ID -tokens](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) (not -access tokens) for [Google OAuth -2.0](https://cloud.google.com/apigee/docs/api-platform/security/oauth/oauth-home). +Currently, the primary mechanism involves passing **OIDC ID Tokens** (typically +obtained via Google OAuth 2.0) for specific parameters marked as authenticated +in the tool's definition within the Toolbox service. Refer to the [Toolbox +Service Documentation - Authenticated +Parameters](https://googleapis.github.io/genai-toolbox/resources/tools/#authenticated-parameters) +for details on configuring this in the service. -### Configure Tools +#### SDK Configuration -Refer to [these -instructions](https://googleapis.github.io/genai-toolbox/resources/tools/#authenticated-parameters) on -configuring tools for authenticated parameters. +> [!WARNING] +> Always use HTTPS to connect your application with the Toolbox service, +> especially in production environments or whenever the communication involves +> sensitive data (including scenarios where tools require authentication +> tokens). Using plain HTTP lacks encryption and exposes your application and +> data to significant security risks. -### Configure SDK +You need to provide the SDK with a function (sync or async) that can retrieve +the required ID token when the tool is called. This function is registered with +the SDK, associating it with the specific authentication requirement defined in +the Toolbox service (matched by name). -You need a method to retrieve an ID token from your authentication service: +```python +from toolbox_core import ToolboxClient -```py async def get_auth_token(): # ... Logic to retrieve ID token (e.g., from local storage, OAuth flow) # This example just returns a placeholder. Replace with your actual token retrieval. return "YOUR_ID_TOKEN" # Placeholder -``` - -#### Add Authentication to a Tool -```py toolbox = ToolboxClient("http://127.0.0.1:5000") -tools = toolbox.load_toolset() - -auth_tool = tools[0].add_auth_token("my_auth", get_auth_token) # Single token +tool = await toolbox.load_tool("my-tool") -multi_auth_tool = tools[0].add_auth_tokens({"my_auth", get_auth_token}) # Multiple tokens +auth_tool = tool.add_auth_token_getters({"my_auth": get_auth_token}) # OR -auth_tools = [tool.add_auth_token("my_auth", get_auth_token) for tool in tools] -``` - -#### Add Authentication While Loading - -```py -auth_tool = toolbox.load_tool(auth_tokens={"my_auth": get_auth_token}) +auth_tool = await toolbox.load_tool("my-tool", auth_token_getters={"my_auth": get_auth_token}) -auth_tools = toolbox.load_toolset(auth_tokens={"my_auth": get_auth_token}) +result = auth_tool(input="some input") ``` -> [!NOTE] -> Adding auth tokens during loading only affect the tools loaded within -> that call. +> [!TIP] +> Your token retriever function is invoked every time an authenticated parameter +> requires a token for a tool call. Consider implementing caching logic within +> this function to avoid redundant token fetching or generation, especially for +> tokens with longer validity periods or if the retrieval process is +> resource-intensive. -### Complete Example +### Binding Parameter Values -```py -import asyncio -from toolbox_langchain import ToolboxClient +Pre-set specific parameter values for a tool *before* it's invoked or passed to +an LLM. Bound values are fixed and won't be requested from the LLM. -async def get_auth_token(): - # ... Logic to retrieve ID token (e.g., from local storage, OAuth flow) - # This example just returns a placeholder. Replace with your actual token retrieval. - return "YOUR_ID_TOKEN" # Placeholder +#### Why Bind Parameters? -toolbox = ToolboxClient("http://127.0.0.1:5000") -tool = toolbox.load_tool("my-tool") + * **Protecting sensitive information:** API keys, secrets, etc. + * **Enforcing consistency:** Ensuring specific values for certain parameters. + * **Pre-filling known data:** Pre-fill common or known values. -auth_tool = tool.add_auth_token("my_auth", get_auth_token) -result = auth_tool.invoke({"input": "some input"}) -print(result) -``` +#### SDK Configuration -## Binding Parameter Values - -Predetermine values for tool parameters using the SDK. These values won't be -modified by the LLM. This is useful for: +> [!IMPORTANT] +> The parameter names used for binding must exactly match the parameter names +> defined in the tool's configuration within the Toolbox service. -* **Protecting sensitive information:** API keys, secrets, etc. -* **Enforcing consistency:** Ensuring specific values for certain parameters. -* **Pre-filling known data:** Providing defaults or context. +> [!NOTE] +> You do not need to modify the tool's configuration in the Toolbox service to +> bind parameter values using the SDK. -### Binding Parameters to a Tool +Similar to authentication, you can bind parameters after loading a tool or +during the loading process. ```py -toolbox = ToolboxClient("http://127.0.0.1:5000") -tools = toolbox.load_toolset() +from toolbox_core import ToolboxClient -bound_tool = tool[0].bind_param("param", "value") # Single param +toolbox = ToolboxClient("http://127.0.0.1:5000") +tool = await toolbox.load_tool("my-tool") -multi_bound_tool = tools[0].bind_params({"param1": "value1", "param2": "value2"}) # Multiple params +bound_tool = tool.bind_parameters({"param": "value"}) # OR -bound_tools = [tool.bind_param("param", "value") for tool in tools] -``` - -### Binding Parameters While Loading - -```py -bound_tool = toolbox.load_tool("my-tool", bound_params={"param": "value"}) - -bound_tools = toolbox.load_toolset(bound_params={"param": "value"}) +bound_tool = await toolbox.load_tool("my-tool", bound_params={"param": "value"}) ``` -> [!NOTE] -> Bound values during loading only affect the tools loaded in that call. +## Framework-Specific Usage -### Binding Dynamic Values +While the core concepts are similar, the way you integrate and use the tools +varies depending on the chosen SDK package and framework. -Use a function to bind dynamic values: +### Using `toolbox-core` -```py -def get_dynamic_value(): - # Logic to determine the value - return "dynamic_value" + * Ideal for framework-agnostic applications or custom orchestration logic. + * Use `ToolboxClient` (async) or `ToolboxSyncClient` (sync). + * Loaded tools (`ToolboxTool`/`ToolboxSyncTool`) are directly + callable/awaitable. + * For integration with frameworks like LangGraph that expect specific tool + formats (e.g., with parsed docstrings for LLM use), you might need to wrap + the loaded tools (e.g., using LangChain's + `StructuredTool.from_function(tool, parse_docstring=True)` as shown in the + `toolbox-core` README). + * See the [toolbox-core + README](https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main/packages/toolbox-core#use-with-langgraph) + for detailed examples. -dynamic_bound_tool = tool.bind_param("param", get_dynamic_value) -``` +### Using `toolbox-langchain` -> [!IMPORTANT] -> You don't need to modify tool configurations to bind parameter values. + * Designed for seamless use within the LangChain/LangGraph ecosystem. + * Use `ToolboxClient` (provides both sync `load_*` and async `aload_*` + methods). + * Loaded tools are LangChain `BaseTool` compatible objects. + * Directly usable with LangGraph agents (`model.bind_tools(tools)` and + `ToolNode(tools)`). + * See the [toolbox-langchain + README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain#use-with-langgraph) + for specific LangGraph integration examples. -## Asynchronous Usage +## Contributing -For better performance through [cooperative -multitasking](https://en.wikipedia.org/wiki/Cooperative_multitasking), you can -use the asynchronous interfaces of the `ToolboxClient`. +Contributions are welcome! Please refer to the +[`CONTRIBUTING.md`](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/CONTRIBUTING.md) +to get started. -> [!Note] -> Asynchronous interfaces like `aload_tool` and `aload_toolset` require an -> asynchronous environment. For guidance on running asynchronous Python -> programs, see [asyncio -> documentation](https://docs.python.org/3/library/asyncio-runner.html#running-an-asyncio-program). +## License -```py -import asyncio -from toolbox_langchain import ToolboxClient +This project is licensed under the Apache License 2.0. See the +[LICENSE](https://github.com/googleapis/genai-toolbox/blob/main/LICENSE) file +for details. -async def main(): - toolbox = ToolboxClient("http://127.0.0.1:5000") - tool = await client.aload_tool("my-tool") - tools = await client.aload_toolset() - response = await tool.ainvoke() +## Support -if __name__ == "__main__": - asyncio.run(main()) -``` +If you encounter issues or have questions, please check the existing [GitHub +Issues](https://github.com/googleapis/genai-toolbox/issues) for the main Toolbox +project. If your issue is specific to one of the SDKs and not found, feel free +to open a new issue in that repository. From e063289841b55e5b38dc1762ce5ad0112afde11b Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Thu, 10 Apr 2025 14:35:30 +0530 Subject: [PATCH 02/14] doc: Improve comparison sections with markdown tables. --- README.md | 113 +++++++++++++++++++++--------------------------------- 1 file changed, 44 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 56a695da..66dedf63 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,6 @@ Databases or APIs) managed by Toolbox into your GenAI applications. - [Overview](#overview) - [Available Packages](#available-packages) - - [toolbox-core](#toolbox-core) - - [toolbox-langchain](#toolbox-langchain) - [Quickstart](#quickstart) - [Core Concepts](#core-concepts) - [Connecting to Toolbox](#connecting-to-toolbox) @@ -31,64 +29,54 @@ Databases or APIs) managed by Toolbox into your GenAI applications. - [Synchronous vs. Asynchronous Usage](#synchronous-vs-asynchronous-usage) - [Authenticating Tools](#authenticating-tools) - [When is Authentication Needed?](#when-is-authentication-needed) - - [Supported Authentication - Mechanisms](#supported-authentication-mechanisms) + - [Supported Authentication Mechanisms](#supported-authentication-mechanisms) - [SDK Configuration](#sdk-configuration) - [Binding Parameter Values](#binding-parameter-values) - [Why Bind Parameters?](#why-bind-parameters) - [SDK Configuration](#sdk-configuration) -- [Framework-Specific Usage](#framework-specific-usage) - - [Using toolbox-core](#using-toolbox-core) - - [Using toolbox-langchain](#using-toolbox-langchain) +- [Usage Comparison](#usage-comparison) - [Contributing](#contributing) - [License](#license) - [Support](#support) - ## Overview The Toolbox service provides a centralized way to manage and expose tools for -use by LLMs. These SDKs act as clients for that service, abstracting away the +use by LLMs. + +- [MCP Toolbox SDKs for Python](#mcp-toolbox-sdks-for-python) + - [Overview](#overview) + - [Available Packages](#available-packages) + - [Quickstart](#quickstart) + - [Core Concepts](#core-concepts) + - [Connecting to Toolbox](#connecting-to-toolbox) + - [Loading Tools](#loading-tools) + - [Invoking Tools](#invoking-tools) + - [Synchronous vs. Asynchronous Usage](#synchronous-vs-asynchronous-usage) + - [Authenticating Tools](#authenticating-tools) + - [When is Authentication Needed?](#when-is-authentication-needed) + - [Supported Authentication Mechanisms](#supported-authentication-mechanisms) + - [SDK Configuration](#sdk-configuration) + - [Binding Parameter Values](#binding-parameter-values) + - [Why Bind Parameters?](#why-bind-parameters) + - [SDK Configuration](#sdk-configuration) + - [Usage Comparison](#usage-comparison) + - [Contributing](#contributing) + - [License](#license) + - [Support](#support) + +These SDKs act as clients for that service, abstracting away the API calls needed to fetch tool definitions and invoke them. ## Available Packages This repository hosts the following Python packages: -### `toolbox-core` - -[![PyPI -version](https://badge.fury.io/py/toolbox-core.svg)](https://badge.fury.io/py/toolbox-core) - -* **Path:** `packages/toolbox-core/` -* **Description:** A framework-agnostic SDK. Provides core functionality - (`ToolboxClient` and `ToolboxTool`) to load and invoke tools. Can be used - directly, with custom frameworks. -* **Details:** [See `toolbox-core` - README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/README.md) - -### `toolbox-langchain` - -[![PyPI -version](https://badge.fury.io/py/toolbox-langchain.svg)](https://badge.fury.io/py/toolbox-langchain) - -* **Path:** `packages/toolbox-langchain/` -* **Description:** Integrates Toolbox tools seamlessly with the - [LangChain](https://python.langchain.com/) ecosystem. Loaded tools are - compatible with LangGraph agents. -* **Details:** [See `toolbox-langchain` - README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain/README.md) - -Install the desired package(s) using pip: - -```bash -# For the core, framework-agnostic SDK -pip install toolbox-core - -# For LangChain/LangGraph integration -pip install toolbox-langchain -``` +| Package | Key Purpose | Integration | Path | Details (README) | PyPI Status | +| :------ | :---------- | :---------- | :---------------------- | :---------- | :--------- +| `toolbox-core` | Provides core, framework-agnostic tool handling | Use directly / Custom | `packages/toolbox-core/` | 📄 [View README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/README.md) | [![PyPI version](https://badge.fury.io/py/toolbox-core.svg)](https://badge.fury.io/py/toolbox-core) | +| `toolbox-langchain` | Integrates Toolbox tools with the LangChain ecoystem | LangChain / LangGraph | `packages/toolbox-langchain/` | 📄 [View README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain/README.md) | [![PyPI version](https://badge.fury.io/py/toolbox-langchain.svg)](https://badge.fury.io/py/toolbox-langchain) | ## Quickstart @@ -105,7 +93,12 @@ To get started using Toolbox tools with an application, follow these general ste Install the appropriate Python SDK package: ```bash + # For the core, framework-agnostic SDK pip install toolbox-core + + # OR + + # For LangChain/LangGraph integration # pip install toolbox-langchain ``` @@ -309,37 +302,19 @@ bound_tool = tool.bind_parameters({"param": "value"}) bound_tool = await toolbox.load_tool("my-tool", bound_params={"param": "value"}) ``` -## Framework-Specific Usage +## Usage Comparison While the core concepts are similar, the way you integrate and use the tools varies depending on the chosen SDK package and framework. -### Using `toolbox-core` - - * Ideal for framework-agnostic applications or custom orchestration logic. - * Use `ToolboxClient` (async) or `ToolboxSyncClient` (sync). - * Loaded tools (`ToolboxTool`/`ToolboxSyncTool`) are directly - callable/awaitable. - * For integration with frameworks like LangGraph that expect specific tool - formats (e.g., with parsed docstrings for LLM use), you might need to wrap - the loaded tools (e.g., using LangChain's - `StructuredTool.from_function(tool, parse_docstring=True)` as shown in the - `toolbox-core` README). - * See the [toolbox-core - README](https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main/packages/toolbox-core#use-with-langgraph) - for detailed examples. - -### Using `toolbox-langchain` - - * Designed for seamless use within the LangChain/LangGraph ecosystem. - * Use `ToolboxClient` (provides both sync `load_*` and async `aload_*` - methods). - * Loaded tools are LangChain `BaseTool` compatible objects. - * Directly usable with LangGraph agents (`model.bind_tools(tools)` and - `ToolNode(tools)`). - * See the [toolbox-langchain - README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain#use-with-langgraph) - for specific LangGraph integration examples. +| Feature | `toolbox-core` | `toolbox-langchain` | +| :---------------------- | :--------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | +| **Target Scenario** | Framework-agnostic applications or custom orchestration logic. | Applications within the LangChain / LangGraph ecosystem. | +| **Client Class(es)** | `ToolboxClient` (async)
`ToolboxSyncClient` (sync) | `ToolboxClient` (provides both sync `load_*` and async `aload_*` methods) | +| **Loaded Tool Type** | `ToolboxTool` (async) /
`ToolboxSyncTool` (sync) | `ToolboxTool` instances (compatible with LangChain `BaseTool`) | +| **Tool Invocation** | Directly callable / awaitable. | Standard LangChain tool usage patterns. | +| **LangGraph Integration** | **Requires wrapping:** Tools may need manual wrapping (e.g., `StructuredTool.from_function`) for full LLM use. | **Seamless:** Designed for direct use with `model.bind_tools(tools)` and `ToolNode(tools)`. | +| **Detailed Examples** | [Core README Section](https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main/packages/toolbox-core#use-with-langgraph) | [LangChain README Section](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain#use-with-langgraph) | ## Contributing From c262ee9a84fdce6af56acaea9bdf46d26b0be6f6 Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Thu, 10 Apr 2025 16:35:32 +0530 Subject: [PATCH 03/14] doc: Add note for upcoming feature of default toolset. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 66dedf63..e8e945c7 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,10 @@ tools = await client.load_toolset("my-toolset") all_tools = await client.load_toolset() ``` +> [!NOTE] +> **Upcoming Feature (`v0.2.0`+):** Support for calling `toolbox.load_toolset()` +> without arguments will be introduced in `v0.2.0`. + ### Invoking Tools Loaded tools behave like callable Python objects or functions. From 0cf18e53da91c44e9f2fc32a2b31fe1e87b62bda Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Thu, 10 Apr 2025 16:35:49 +0530 Subject: [PATCH 04/14] doc: Improve sync/async section --- README.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e8e945c7..56482e87 100644 --- a/README.md +++ b/README.md @@ -181,16 +181,12 @@ result = await tool.ainvoke({"param1": "value1", "param2": "value2"}) ### Synchronous vs. Asynchronous Usage - * **Asynchronous (Recommended for I/O):** Most SDKs prioritize asynchronous - operations (`async`/`await`) for better performance in I/O-bound tasks like - network calls to the Toolbox service. This requires running your code within - an async event loop (e.g., using `asyncio.run()`). The default - `toolbox-core` `ToolboxClient` is async. The `toolbox-langchain` package - provides async methods like `aload_tool`, `aload_toolset`, `ainvoke`. - * **Synchronous:** For simpler scripts or applications not built around - asyncio, synchronous alternatives are provided. `toolbox-core` offers - `ToolboxSyncClient` and `ToolboxSyncTool`. `toolbox-langchain` provides - synchronous methods like `load_tool`, `load_toolset`, and `invoke`. +| Package | Loading Tools | Invoking Tools | Docs | +| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | +| **`toolbox-core`** | **Async:**
*  **Class:** `ToolboxClient`
*  **Methods:**
   *  `await client.load_tool()`
   *  `await client.load_toolset()`

**Sync:**
*  **Class:** `ToolboxSyncClient`
*  **Methods:**
   *  `client.load_tool()`
   *  `client.load_toolset()` | **Async:**
*  **Class:** `ToolboxTool`
*  **Invocation:** `await tool(...)`

**Sync:**
*  **Class:** `ToolboxSyncTool`
*  **Invocation:** `tool(...)` | [`toolbox-core` README](./packages/toolbox-core/README.md) | +| **`toolbox-langchain`** | **Unified Class:** `ToolboxClient`

*  **Async Methods:**
   *  `await client.aload_tool()`
   *  `await client.aload_toolset()`

*  **Sync Methods:**
   *  `client.load_tool()`
   *  `client.load_toolset()` | **Unified Class:** `ToolboxTool`

*  **Async Method:** `await tool.ainvoke()`

*  **Sync Method:** `tool.invoke()` | [`toolbox-langchain` README](./packages/toolbox-langchain/README.md#synchronous-and-asynchronous-usage) | + +The `ToolboxSyncClient` handles communication with the Toolbox service *synchronously* and produces `ToolboxSyncTool` instances when you load tools. You do not use the `await` keyword when interacting with these synchronous versions. ```py from toolbox_core import ToolboxSyncClient @@ -272,7 +268,7 @@ result = auth_tool(input="some input") ### Binding Parameter Values Pre-set specific parameter values for a tool *before* it's invoked or passed to -an LLM. Bound values are fixed and won't be requested from the LLM. +an LLM. Bound parameter values are fixed and won't be requested from the LLM. #### Why Bind Parameters? From b0524d5427f635432b2623a2c75336de1bc637f1 Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Thu, 10 Apr 2025 16:40:36 +0530 Subject: [PATCH 05/14] doc: Improve links in sync/async section --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 56482e87..d372c2a5 100644 --- a/README.md +++ b/README.md @@ -183,8 +183,8 @@ result = await tool.ainvoke({"param1": "value1", "param2": "value2"}) | Package | Loading Tools | Invoking Tools | Docs | | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | -| **`toolbox-core`** | **Async:**
*  **Class:** `ToolboxClient`
*  **Methods:**
   *  `await client.load_tool()`
   *  `await client.load_toolset()`

**Sync:**
*  **Class:** `ToolboxSyncClient`
*  **Methods:**
   *  `client.load_tool()`
   *  `client.load_toolset()` | **Async:**
*  **Class:** `ToolboxTool`
*  **Invocation:** `await tool(...)`

**Sync:**
*  **Class:** `ToolboxSyncTool`
*  **Invocation:** `tool(...)` | [`toolbox-core` README](./packages/toolbox-core/README.md) | -| **`toolbox-langchain`** | **Unified Class:** `ToolboxClient`

*  **Async Methods:**
   *  `await client.aload_tool()`
   *  `await client.aload_toolset()`

*  **Sync Methods:**
   *  `client.load_tool()`
   *  `client.load_toolset()` | **Unified Class:** `ToolboxTool`

*  **Async Method:** `await tool.ainvoke()`

*  **Sync Method:** `tool.invoke()` | [`toolbox-langchain` README](./packages/toolbox-langchain/README.md#synchronous-and-asynchronous-usage) | +| **`toolbox-core`** | **Async:**
*  **Class:** `ToolboxClient`
*  **Methods:**
   *  `await client.load_tool()`
   *  `await client.load_toolset()`

**Sync:**
*  **Class:** `ToolboxSyncClient`
*  **Methods:**
   *  `client.load_tool()`
   *  `client.load_toolset()` | **Async:**
*  **Class:** `ToolboxTool`
*  **Invocation:** `await tool(...)`

**Sync:**
*  **Class:** `ToolboxSyncTool`
*  **Invocation:** `tool(...)` | [`toolbox-core` README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/README.md#synchronous-usage) | +| **`toolbox-langchain`** | **Unified Class:** `ToolboxClient`

*  **Async Methods:**
   *  `await client.aload_tool()`
   *  `await client.aload_toolset()`

*  **Sync Methods:**
   *  `client.load_tool()`
   *  `client.load_toolset()` | **Unified Class:** `ToolboxTool`

*  **Async Method:** `await tool.ainvoke()`

*  **Sync Method:** `tool.invoke()` | [`toolbox-langchain` README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain/README.md#asynchronous-usage) | The `ToolboxSyncClient` handles communication with the Toolbox service *synchronously* and produces `ToolboxSyncTool` instances when you load tools. You do not use the `await` keyword when interacting with these synchronous versions. From 7397e58ebd8c65daa6e0f7a6ed0869b4e87964bb Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Thu, 10 Apr 2025 16:46:26 +0530 Subject: [PATCH 06/14] doc: Simplify sync/async section table. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d372c2a5..3c9a74a3 100644 --- a/README.md +++ b/README.md @@ -183,8 +183,8 @@ result = await tool.ainvoke({"param1": "value1", "param2": "value2"}) | Package | Loading Tools | Invoking Tools | Docs | | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | -| **`toolbox-core`** | **Async:**
*  **Class:** `ToolboxClient`
*  **Methods:**
   *  `await client.load_tool()`
   *  `await client.load_toolset()`

**Sync:**
*  **Class:** `ToolboxSyncClient`
*  **Methods:**
   *  `client.load_tool()`
   *  `client.load_toolset()` | **Async:**
*  **Class:** `ToolboxTool`
*  **Invocation:** `await tool(...)`

**Sync:**
*  **Class:** `ToolboxSyncTool`
*  **Invocation:** `tool(...)` | [`toolbox-core` README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/README.md#synchronous-usage) | -| **`toolbox-langchain`** | **Unified Class:** `ToolboxClient`

*  **Async Methods:**
   *  `await client.aload_tool()`
   *  `await client.aload_toolset()`

*  **Sync Methods:**
   *  `client.load_tool()`
   *  `client.load_toolset()` | **Unified Class:** `ToolboxTool`

*  **Async Method:** `await tool.ainvoke()`

*  **Sync Method:** `tool.invoke()` | [`toolbox-langchain` README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain/README.md#asynchronous-usage) | +| **`toolbox-core`** | **Async:**
* **Class:** `ToolboxClient`
* **Methods:** `await client.load_*`

**Sync:**
* **Class:** `ToolboxSyncClient`
* **Methods:** `client.load_*` | **Async:**
* **Class:** `ToolboxTool`
* **Invocation:** `await tool(...)`

**Sync:**
* **Class:** `ToolboxSyncTool`
* **Invocation:** `tool(...)` | [`toolbox-core` README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/README.md#synchronous-usage) | +| **`toolbox-langchain`** | **Unified Class:** `ToolboxClient`

* **Async Methods:** `await client.aload_*`

* **Sync Methods:** `client.load_*` | **Unified Class:** `ToolboxTool`

*  **Async Method:** `await tool.ainvoke()`

*  **Sync Method:** `tool.invoke()` | [`toolbox-langchain` README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain/README.md#asynchronous-usage) | The `ToolboxSyncClient` handles communication with the Toolbox service *synchronously* and produces `ToolboxSyncTool` instances when you load tools. You do not use the `await` keyword when interacting with these synchronous versions. From 5fd1b243102a1d99d2a036363141553d3a840f40 Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Thu, 10 Apr 2025 16:57:58 +0530 Subject: [PATCH 07/14] doc: Improve support section --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3c9a74a3..c13607a3 100644 --- a/README.md +++ b/README.md @@ -332,5 +332,6 @@ for details. If you encounter issues or have questions, please check the existing [GitHub Issues](https://github.com/googleapis/genai-toolbox/issues) for the main Toolbox -project. If your issue is specific to one of the SDKs and not found, feel free -to open a new issue in that repository. +project. If your issue is specific to one of the SDKs, please look for existing +issues [here](https://github.com/googleapis/mcp-toolbox-sdk-python/issues) or +open a new issue in that repository. \ No newline at end of file From 4a2bc2a89ca27850c301b3f02806ca132ef6ab1e Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Thu, 10 Apr 2025 23:30:56 +0530 Subject: [PATCH 08/14] doc: Add load and invocations for toolbox langchain as well --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c13607a3..d787fefa 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,7 @@ the Toolbox service (matched by name). ```python from toolbox_core import ToolboxClient +# from toolbox_langchain import ToolboxClient async def get_auth_token(): # ... Logic to retrieve ID token (e.g., from local storage, OAuth flow) @@ -248,14 +249,18 @@ async def get_auth_token(): toolbox = ToolboxClient("http://127.0.0.1:5000") tool = await toolbox.load_tool("my-tool") +# tool = await toolbox.aload_tool("my-tool") auth_tool = tool.add_auth_token_getters({"my_auth": get_auth_token}) +# auth_tool = tool.add_auth_tokens({"my_auth": get_auth_token}) # OR auth_tool = await toolbox.load_tool("my-tool", auth_token_getters={"my_auth": get_auth_token}) +# auth_tool = await toolbox.aload_tool("my-tool", auth_tokens={"my_auth": get_auth_token}) -result = auth_tool(input="some input") +result = await auth_tool(input="some input") +# result = await auth_tool.ainvoke({"input": "some input"}) ``` > [!TIP] @@ -291,15 +296,19 @@ during the loading process. ```py from toolbox_core import ToolboxClient +# from toolbox_langchain import ToolboxClient toolbox = ToolboxClient("http://127.0.0.1:5000") tool = await toolbox.load_tool("my-tool") +# tool = await toolbox.aload_tool("my-tool") bound_tool = tool.bind_parameters({"param": "value"}) +# bound_tool = tool.bind_params({"param": "value"}) # OR bound_tool = await toolbox.load_tool("my-tool", bound_params={"param": "value"}) +# bound_tool = await toolbox.aload_tool("my-tool", bound_params={"param": "value"}) ``` ## Usage Comparison From 54c50b2e22965f677eb3daecb3d459e48e8b4659 Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Fri, 11 Apr 2025 17:35:14 +0530 Subject: [PATCH 09/14] doc: Fix TOC --- README.md | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index d787fefa..2fa24c31 100644 --- a/README.md +++ b/README.md @@ -40,33 +40,11 @@ Databases or APIs) managed by Toolbox into your GenAI applications. - [Support](#support) + ## Overview The Toolbox service provides a centralized way to manage and expose tools for -use by LLMs. - -- [MCP Toolbox SDKs for Python](#mcp-toolbox-sdks-for-python) - - [Overview](#overview) - - [Available Packages](#available-packages) - - [Quickstart](#quickstart) - - [Core Concepts](#core-concepts) - - [Connecting to Toolbox](#connecting-to-toolbox) - - [Loading Tools](#loading-tools) - - [Invoking Tools](#invoking-tools) - - [Synchronous vs. Asynchronous Usage](#synchronous-vs-asynchronous-usage) - - [Authenticating Tools](#authenticating-tools) - - [When is Authentication Needed?](#when-is-authentication-needed) - - [Supported Authentication Mechanisms](#supported-authentication-mechanisms) - - [SDK Configuration](#sdk-configuration) - - [Binding Parameter Values](#binding-parameter-values) - - [Why Bind Parameters?](#why-bind-parameters) - - [SDK Configuration](#sdk-configuration) - - [Usage Comparison](#usage-comparison) - - [Contributing](#contributing) - - [License](#license) - - [Support](#support) - -These SDKs act as clients for that service, abstracting away the +use by LLMs. These SDKs act as clients for that service, abstracting away the API calls needed to fetch tool definitions and invoke them. ## Available Packages From cfc1efacf2fc976ba9eec934c71deb7d42770aae Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Fri, 11 Apr 2025 17:37:45 +0530 Subject: [PATCH 10/14] doc: Improve README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fa24c31..940865d2 100644 --- a/README.md +++ b/README.md @@ -321,4 +321,4 @@ If you encounter issues or have questions, please check the existing [GitHub Issues](https://github.com/googleapis/genai-toolbox/issues) for the main Toolbox project. If your issue is specific to one of the SDKs, please look for existing issues [here](https://github.com/googleapis/mcp-toolbox-sdk-python/issues) or -open a new issue in that repository. \ No newline at end of file +open a new issue in this repository. \ No newline at end of file From e6d9d5759377b6079d093721b4e80cccd16b3053 Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Fri, 11 Apr 2025 17:40:08 +0530 Subject: [PATCH 11/14] doc: Add note for the upcoming default toolset feature. --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 940865d2..6d90a9b9 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ all_tools = await client.load_toolset() ``` > [!NOTE] -> **Upcoming Feature (`v0.2.0`+):** Support for calling `toolbox.load_toolset()` +> **Upcoming Feature (`v0.2.0`+):** Support for calling `client.load_toolset()` > without arguments will be introduced in `v0.2.0`. ### Invoking Tools @@ -182,6 +182,11 @@ tools = sync_client.load_toolset("my-toolset") all_tools = sync_client.load_toolset() ``` +> [!NOTE] +> **Upcoming Feature (`v0.2.0`+):** Support for calling `sync_client.load_toolset()` +> without arguments will be introduced in `v0.2.0`. + + ### Authenticating Tools Tools configured in the Toolbox service to require authentication need From a3a7416b0210f92c683a6cfb40bee23bc9030504 Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Fri, 11 Apr 2025 17:43:37 +0530 Subject: [PATCH 12/14] doc: Improve README --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6d90a9b9..bbf9550c 100644 --- a/README.md +++ b/README.md @@ -145,17 +145,16 @@ Loaded tools behave like callable Python objects or functions. * **`toolbox-core`**: Async tools are `awaitable`, sync tools are called directly. + ```py + result = await tool(param1="value1", param2="value2") + ``` + * **`toolbox-langchain`**: Tools conform to LangChain's `BaseTool` interface and are typically invoked via `.invoke()` or `.ainvoke()`, often managed by a LangGraph agent. - -```py -# toolbox-core -result = await tool(param1="value1", param2="value2") - -# toolbox-langchain -result = await tool.ainvoke({"param1": "value1", "param2": "value2"}) -``` + ```py + result = await tool.ainvoke({"param1": "value1", "param2": "value2"}) + ``` ### Synchronous vs. Asynchronous Usage From 5ca7630df80e91deeb1c68d3818bc391f0d56fbe Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Fri, 11 Apr 2025 17:59:36 +0530 Subject: [PATCH 13/14] doc: Improve usage comparison table. --- README.md | 51 ++++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index bbf9550c..42e7fef9 100644 --- a/README.md +++ b/README.md @@ -19,25 +19,26 @@ Databases or APIs) managed by Toolbox into your GenAI applications. -- [Overview](#overview) -- [Available Packages](#available-packages) -- [Quickstart](#quickstart) -- [Core Concepts](#core-concepts) - - [Connecting to Toolbox](#connecting-to-toolbox) - - [Loading Tools](#loading-tools) - - [Invoking Tools](#invoking-tools) - - [Synchronous vs. Asynchronous Usage](#synchronous-vs-asynchronous-usage) - - [Authenticating Tools](#authenticating-tools) - - [When is Authentication Needed?](#when-is-authentication-needed) - - [Supported Authentication Mechanisms](#supported-authentication-mechanisms) - - [SDK Configuration](#sdk-configuration) - - [Binding Parameter Values](#binding-parameter-values) - - [Why Bind Parameters?](#why-bind-parameters) - - [SDK Configuration](#sdk-configuration) -- [Usage Comparison](#usage-comparison) -- [Contributing](#contributing) -- [License](#license) -- [Support](#support) +- [MCP Toolbox SDKs for Python](#mcp-toolbox-sdks-for-python) + - [Overview](#overview) + - [Available Packages](#available-packages) + - [Quickstart](#quickstart) + - [Core Concepts](#core-concepts) + - [Connecting to Toolbox](#connecting-to-toolbox) + - [Loading Tools](#loading-tools) + - [Invoking Tools](#invoking-tools) + - [Synchronous vs. Asynchronous Usage](#synchronous-vs-asynchronous-usage) + - [Authenticating Tools](#authenticating-tools) + - [When is Authentication Needed?](#when-is-authentication-needed) + - [Supported Authentication Mechanisms](#supported-authentication-mechanisms) + - [SDK Configuration](#sdk-configuration) + - [Binding Parameter Values](#binding-parameter-values) + - [Why Bind Parameters?](#why-bind-parameters) + - [SDK Configuration](#sdk-configuration) + - [Usage Comparison](#usage-comparison) + - [Contributing](#contributing) + - [License](#license) + - [Support](#support) @@ -298,14 +299,10 @@ bound_tool = await toolbox.load_tool("my-tool", bound_params={"param": "value"}) While the core concepts are similar, the way you integrate and use the tools varies depending on the chosen SDK package and framework. -| Feature | `toolbox-core` | `toolbox-langchain` | -| :---------------------- | :--------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | -| **Target Scenario** | Framework-agnostic applications or custom orchestration logic. | Applications within the LangChain / LangGraph ecosystem. | -| **Client Class(es)** | `ToolboxClient` (async)
`ToolboxSyncClient` (sync) | `ToolboxClient` (provides both sync `load_*` and async `aload_*` methods) | -| **Loaded Tool Type** | `ToolboxTool` (async) /
`ToolboxSyncTool` (sync) | `ToolboxTool` instances (compatible with LangChain `BaseTool`) | -| **Tool Invocation** | Directly callable / awaitable. | Standard LangChain tool usage patterns. | -| **LangGraph Integration** | **Requires wrapping:** Tools may need manual wrapping (e.g., `StructuredTool.from_function`) for full LLM use. | **Seamless:** Designed for direct use with `model.bind_tools(tools)` and `ToolNode(tools)`. | -| **Detailed Examples** | [Core README Section](https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main/packages/toolbox-core#use-with-langgraph) | [LangChain README Section](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain#use-with-langgraph) | +| Package | Target Scenario | Client Class(es) | Loaded Tool Type | Tool Invocation | LangGraph Integration | Detailed Examples | +| :---------------- | :--------------------------------------------------------------- | :---------------------------------------------------------------------------------- | :--------------------------------------------------------------------- | :-------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | +| `toolbox-core` | Framework-agnostic applications or custom orchestration logic. | `ToolboxClient` (async)
`ToolboxSyncClient` (sync) | `ToolboxTool` (async) /
`ToolboxSyncTool` (sync) | Directly callable / awaitable. | **Requires wrapping:** Tools may need manual wrapping (e.g., `StructuredTool.from_function`) for full LLM use. | [Core README Section](https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main/packages/toolbox-core#use-with-langgraph) | +| `toolbox-langchain` | Applications within the LangChain / LangGraph ecosystem. | `ToolboxClient` (provides both sync `load_*` and async `aload_*` methods) | `ToolboxTool` instances (compatible with LangChain `BaseTool`) | Standard LangChain tool usage patterns. | **Seamless:** Designed for direct use with `model.bind_tools(tools)` and `ToolNode(tools)`. | [LangChain README Section](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain#use-with-langgraph) | ## Contributing From 8f0460f27aa9fec810206c9c8dd7ad9ba40c5eb9 Mon Sep 17 00:00:00 2001 From: Anubhav Dhawan Date: Tue, 22 Apr 2025 12:15:20 +0530 Subject: [PATCH 14/14] docs: Update repo-level README to only focus on which package to use. --- README.md | 309 ++++++++++-------------------------------------------- 1 file changed, 55 insertions(+), 254 deletions(-) diff --git a/README.md b/README.md index 42e7fef9..c6292082 100644 --- a/README.md +++ b/README.md @@ -19,291 +19,92 @@ Databases or APIs) managed by Toolbox into your GenAI applications. -- [MCP Toolbox SDKs for Python](#mcp-toolbox-sdks-for-python) - - [Overview](#overview) - - [Available Packages](#available-packages) - - [Quickstart](#quickstart) - - [Core Concepts](#core-concepts) - - [Connecting to Toolbox](#connecting-to-toolbox) - - [Loading Tools](#loading-tools) - - [Invoking Tools](#invoking-tools) - - [Synchronous vs. Asynchronous Usage](#synchronous-vs-asynchronous-usage) - - [Authenticating Tools](#authenticating-tools) - - [When is Authentication Needed?](#when-is-authentication-needed) - - [Supported Authentication Mechanisms](#supported-authentication-mechanisms) - - [SDK Configuration](#sdk-configuration) - - [Binding Parameter Values](#binding-parameter-values) - - [Why Bind Parameters?](#why-bind-parameters) - - [SDK Configuration](#sdk-configuration) - - [Usage Comparison](#usage-comparison) - - [Contributing](#contributing) - - [License](#license) - - [Support](#support) +- [Overview](#overview) +- [Which Package Should I Use?](#which-package-should-i-use) +- [Available Packages](#available-packages) +- [Getting Started](#getting-started) +- [Contributing](#contributing) +- [License](#license) +- [Support](#support) ## Overview -The Toolbox service provides a centralized way to manage and expose tools for -use by LLMs. These SDKs act as clients for that service, abstracting away the -API calls needed to fetch tool definitions and invoke them. +The MCP Toolbox service provides a centralized way to manage and expose tools +(like API connectors, database query tools, etc.) for use by GenAI applications. + +These Python SDKs act as clients for that service. They handle the communication needed to: + +* Fetch tool definitions from your running Toolbox instance. +* Provide convenient Python objects or functions representing those tools. +* Invoke the tools (calling the underlying APIs/services configured in Toolbox). +* Handle authentication and parameter binding as needed. + +By using these SDKs, you can easily leverage your Toolbox-managed tools directly +within your Python applications or AI orchestration frameworks. + +## Which Package Should I Use? + +Choosing the right package depends on how you are building your application: + +* [`toolbox-langchain`](https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main/packages/toolbox-langchain): + Use this package if you are building your application using the LangChain or + LangGraph frameworks. It provides tools that are directly compatible with the + LangChain ecosystem (`BaseTool` interface), simplifying integration. +* [`toolbox-core`](https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main/packages/toolbox-core): + Use this package if you are not using LangChain/LangGraph or any other + orchestration framework, or if you need a framework-agnostic way to interact + with Toolbox tools (e.g., for custom orchestration logic or direct use in + Python scripts). ## Available Packages -This repository hosts the following Python packages: +This repository hosts the following Python packages. See the package-specific +README for detailed installation and usage instructions: -| Package | Key Purpose | Integration | Path | Details (README) | PyPI Status | +| Package | Target Use Case | Integration | Path | Details (README) | PyPI Status | | :------ | :---------- | :---------- | :---------------------- | :---------- | :--------- -| `toolbox-core` | Provides core, framework-agnostic tool handling | Use directly / Custom | `packages/toolbox-core/` | 📄 [View README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/README.md) | [![PyPI version](https://badge.fury.io/py/toolbox-core.svg)](https://badge.fury.io/py/toolbox-core) | -| `toolbox-langchain` | Integrates Toolbox tools with the LangChain ecoystem | LangChain / LangGraph | `packages/toolbox-langchain/` | 📄 [View README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain/README.md) | [![PyPI version](https://badge.fury.io/py/toolbox-langchain.svg)](https://badge.fury.io/py/toolbox-langchain) | +| `toolbox-core` | Framework-agnostic / Custom applications | Use directly / Custom | `packages/toolbox-core/` | 📄 [View README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/README.md) | [![PyPI version](https://badge.fury.io/py/toolbox-core.svg)](https://badge.fury.io/py/toolbox-core) | +| `toolbox-langchain` | LangChain / LangGraph applications | LangChain / LangGraph | `packages/toolbox-langchain/` | 📄 [View README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain/README.md) | [![PyPI version](https://badge.fury.io/py/toolbox-langchain.svg)](https://badge.fury.io/py/toolbox-langchain) | -## Quickstart +## Getting Started To get started using Toolbox tools with an application, follow these general steps: -1. **Configure and Run the Toolbox Service:** +1. **Set up and Run the Toolbox Service:** - For detailed instructions on setting up and running the Toolbox service - itself, see: [**Toolbox Getting Started + Before using the SDKs, you need the main MCP Toolbox service running. Follow + the instructions here: [**Toolbox Getting Started Guide**](https://github.com/googleapis/genai-toolbox?tab=readme-ov-file#getting-started) -2. **Install the Toolbox SDK:** - - Install the appropriate Python SDK package: +2. **Install the Appropriate SDK:** + + Choose the package based on your needs (see "[Which Package Should I Use?](#which-package-should-i-use)" above) and install it: ```bash # For the core, framework-agnostic SDK pip install toolbox-core # OR - + # For LangChain/LangGraph integration - # pip install toolbox-langchain + pip install toolbox-langchain ``` -3. **Load Tools Using the SDK Client:** +3. **Use the SDK:** - Once the service is running and the SDK is installed, use the - `ToolboxClient` in your Python code to connect to the service and load the - tools. - - ```py - from toolbox_core import ToolboxClient - # from toolbox_langchain import ToolboxClient - - client = ToolboxClient("http://127.0.0.1:5000") - - tools = await client.load_toolset("toolset_name") - # tools = await client.aload_toolset("toolset_name") - ``` + Consult the README for your chosen package (linked in the "[Available + Packages](#available-packages)" section above) for detailed instructions on + how to connect the client, load tool definitions, invoke tools, configure + authentication/binding, and integrate them into your application or + framework. > [!TIP] -> For a complete, step-by-step walkthrough, please refer to the full tutorial: -> [**Toolbox Quickstart +> For a complete, end-to-end example including setting up the service and using +> an SDK, see the full tutorial: [**Toolbox Quickstart > Tutorial**](https://googleapis.github.io/genai-toolbox/getting-started/local_quickstart) -## Core Concepts - -The following concepts apply generally across the different SDK packages, -although specific method names or object types might vary slightly. Refer to the -individual package READMEs for precise details. - -### Connecting to Toolbox - -Initialize a client, pointing it to the URL where your Toolbox service is -running. - -```py -from toolbox_core import ToolboxClient - -# replace with your Toolbox service's URL -client = ToolboxClient("http://127.0.0.1:5000") -``` - -### Loading Tools - -Fetch tool definitions from the Toolbox service. You can load individual tools -by name or load all tools within a specific toolset (or all available toolsets). - -```py -# Load a single tool -tool = await client.load_tool("my-tool") - -# Load all tools in a specific toolset -tools = await client.load_toolset("my-toolset") - -# Load all tools from all toolsets -all_tools = await client.load_toolset() -``` - -> [!NOTE] -> **Upcoming Feature (`v0.2.0`+):** Support for calling `client.load_toolset()` -> without arguments will be introduced in `v0.2.0`. - -### Invoking Tools - -Loaded tools behave like callable Python objects or functions. - - * **`toolbox-core`**: Async tools are `awaitable`, sync tools are called - directly. - ```py - result = await tool(param1="value1", param2="value2") - ``` - - * **`toolbox-langchain`**: Tools conform to LangChain's `BaseTool` interface - and are typically invoked via `.invoke()` or `.ainvoke()`, often managed by - a LangGraph agent. - ```py - result = await tool.ainvoke({"param1": "value1", "param2": "value2"}) - ``` - -### Synchronous vs. Asynchronous Usage - -| Package | Loading Tools | Invoking Tools | Docs | -| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | -| **`toolbox-core`** | **Async:**
* **Class:** `ToolboxClient`
* **Methods:** `await client.load_*`

**Sync:**
* **Class:** `ToolboxSyncClient`
* **Methods:** `client.load_*` | **Async:**
* **Class:** `ToolboxTool`
* **Invocation:** `await tool(...)`

**Sync:**
* **Class:** `ToolboxSyncTool`
* **Invocation:** `tool(...)` | [`toolbox-core` README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/README.md#synchronous-usage) | -| **`toolbox-langchain`** | **Unified Class:** `ToolboxClient`

* **Async Methods:** `await client.aload_*`

* **Sync Methods:** `client.load_*` | **Unified Class:** `ToolboxTool`

*  **Async Method:** `await tool.ainvoke()`

*  **Sync Method:** `tool.invoke()` | [`toolbox-langchain` README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain/README.md#asynchronous-usage) | - -The `ToolboxSyncClient` handles communication with the Toolbox service *synchronously* and produces `ToolboxSyncTool` instances when you load tools. You do not use the `await` keyword when interacting with these synchronous versions. - -```py -from toolbox_core import ToolboxSyncClient - -# replace with your Toolbox service's URL -sync_client = ToolboxSyncClient("http://127.0.0.1:5000") - -# Load a single tool -tool = sync_client.load_tool("my-tool") - -# Load all tools in a specific toolset -tools = sync_client.load_toolset("my-toolset") - -# Load all tools from all toolsets -all_tools = sync_client.load_toolset() -``` - -> [!NOTE] -> **Upcoming Feature (`v0.2.0`+):** Support for calling `sync_client.load_toolset()` -> without arguments will be introduced in `v0.2.0`. - - -### Authenticating Tools - -Tools configured in the Toolbox service to require authentication need -credentials provided by the SDK during invocation. - -#### When is Authentication Needed? - -Authentication is configured *per-tool* within the Toolbox service. If a tool -definition specifies it requires authentication (e.g., an "authenticated -parameter"), the SDK must be configured to provide the necessary token. - -#### Supported Authentication Mechanisms - -Currently, the primary mechanism involves passing **OIDC ID Tokens** (typically -obtained via Google OAuth 2.0) for specific parameters marked as authenticated -in the tool's definition within the Toolbox service. Refer to the [Toolbox -Service Documentation - Authenticated -Parameters](https://googleapis.github.io/genai-toolbox/resources/tools/#authenticated-parameters) -for details on configuring this in the service. - -#### SDK Configuration - -> [!WARNING] -> Always use HTTPS to connect your application with the Toolbox service, -> especially in production environments or whenever the communication involves -> sensitive data (including scenarios where tools require authentication -> tokens). Using plain HTTP lacks encryption and exposes your application and -> data to significant security risks. - -You need to provide the SDK with a function (sync or async) that can retrieve -the required ID token when the tool is called. This function is registered with -the SDK, associating it with the specific authentication requirement defined in -the Toolbox service (matched by name). - -```python -from toolbox_core import ToolboxClient -# from toolbox_langchain import ToolboxClient - -async def get_auth_token(): - # ... Logic to retrieve ID token (e.g., from local storage, OAuth flow) - # This example just returns a placeholder. Replace with your actual token retrieval. - return "YOUR_ID_TOKEN" # Placeholder - -toolbox = ToolboxClient("http://127.0.0.1:5000") -tool = await toolbox.load_tool("my-tool") -# tool = await toolbox.aload_tool("my-tool") - -auth_tool = tool.add_auth_token_getters({"my_auth": get_auth_token}) -# auth_tool = tool.add_auth_tokens({"my_auth": get_auth_token}) - -# OR - -auth_tool = await toolbox.load_tool("my-tool", auth_token_getters={"my_auth": get_auth_token}) -# auth_tool = await toolbox.aload_tool("my-tool", auth_tokens={"my_auth": get_auth_token}) - -result = await auth_tool(input="some input") -# result = await auth_tool.ainvoke({"input": "some input"}) -``` - -> [!TIP] -> Your token retriever function is invoked every time an authenticated parameter -> requires a token for a tool call. Consider implementing caching logic within -> this function to avoid redundant token fetching or generation, especially for -> tokens with longer validity periods or if the retrieval process is -> resource-intensive. - -### Binding Parameter Values - -Pre-set specific parameter values for a tool *before* it's invoked or passed to -an LLM. Bound parameter values are fixed and won't be requested from the LLM. - -#### Why Bind Parameters? - - * **Protecting sensitive information:** API keys, secrets, etc. - * **Enforcing consistency:** Ensuring specific values for certain parameters. - * **Pre-filling known data:** Pre-fill common or known values. - -#### SDK Configuration - -> [!IMPORTANT] -> The parameter names used for binding must exactly match the parameter names -> defined in the tool's configuration within the Toolbox service. - -> [!NOTE] -> You do not need to modify the tool's configuration in the Toolbox service to -> bind parameter values using the SDK. - -Similar to authentication, you can bind parameters after loading a tool or -during the loading process. - -```py -from toolbox_core import ToolboxClient -# from toolbox_langchain import ToolboxClient - -toolbox = ToolboxClient("http://127.0.0.1:5000") -tool = await toolbox.load_tool("my-tool") -# tool = await toolbox.aload_tool("my-tool") - -bound_tool = tool.bind_parameters({"param": "value"}) -# bound_tool = tool.bind_params({"param": "value"}) - -# OR - -bound_tool = await toolbox.load_tool("my-tool", bound_params={"param": "value"}) -# bound_tool = await toolbox.aload_tool("my-tool", bound_params={"param": "value"}) -``` - -## Usage Comparison - -While the core concepts are similar, the way you integrate and use the tools -varies depending on the chosen SDK package and framework. - -| Package | Target Scenario | Client Class(es) | Loaded Tool Type | Tool Invocation | LangGraph Integration | Detailed Examples | -| :---------------- | :--------------------------------------------------------------- | :---------------------------------------------------------------------------------- | :--------------------------------------------------------------------- | :-------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | -| `toolbox-core` | Framework-agnostic applications or custom orchestration logic. | `ToolboxClient` (async)
`ToolboxSyncClient` (sync) | `ToolboxTool` (async) /
`ToolboxSyncTool` (sync) | Directly callable / awaitable. | **Requires wrapping:** Tools may need manual wrapping (e.g., `StructuredTool.from_function`) for full LLM use. | [Core README Section](https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main/packages/toolbox-core#use-with-langgraph) | -| `toolbox-langchain` | Applications within the LangChain / LangGraph ecosystem. | `ToolboxClient` (provides both sync `load_*` and async `aload_*` methods) | `ToolboxTool` instances (compatible with LangChain `BaseTool`) | Standard LangChain tool usage patterns. | **Seamless:** Designed for direct use with `model.bind_tools(tools)` and `ToolNode(tools)`. | [LangChain README Section](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-langchain#use-with-langgraph) | - ## Contributing Contributions are welcome! Please refer to the