diff --git a/docs/en/sdks/python-sdk/_index.md b/docs/en/sdks/python-sdk/_index.md index 352a1607cdf2..033e0ea47714 100644 --- a/docs/en/sdks/python-sdk/_index.md +++ b/docs/en/sdks/python-sdk/_index.md @@ -22,4 +22,77 @@ These Python SDKs act as clients for that service. They handle the communication By using these SDKs, you can easily leverage your Toolbox-managed tools directly within your Python applications or AI orchestration frameworks. -[Github](https://github.com/googleapis/mcp-toolbox-sdk-python) \ No newline at end of file +## Which Package Should I Use? + +Choosing the right package depends on how you are building your application: + +* [`toolbox-langchain`](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-llamaindex`](llamaindex): + Use this package if you are building your application using the LlamaIndex framework. + It provides tools that are directly compatible with the + LlamaIndex ecosystem (`BaseTool` interface), simplifying integration. +* [`toolbox-core`](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). +* [`toolbox-adk`](ADK): + Use this package if you are building your application using Google ADK (Agent Development Kit). + It provides tools that are directly compatible with the + Google ADK ecosystem (`BaseTool` / `BaseToolset` interface) handling authentication propagation, header management, and tool wrapping automatically. + +## Available Packages + +This repository hosts the following Python packages. See the package-specific +README for detailed installation and usage instructions: + +| Package | Target Use Case | Integration | Path | Details (README) | PyPI Status | +| :------ | :---------- | :---------- | :---------------------- | :---------- | :--------- +| `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://img.shields.io/pypi/v/toolbox-core.svg) | +| `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://img.shields.io/pypi/v/toolbox-langchain.svg) | +| `toolbox-llamaindex` | LlamaIndex applications | LlamaIndex | `packages/toolbox-llamaindex/` | 📄 [View README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-llamaindex/README.md) | ![pypi version](https://img.shields.io/pypi/v/toolbox-llamaindex.svg) | +| `toolbox-adk` | Google ADK applications | Google ADK | `packages/toolbox-adk/` | 📄 [View README](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-adk/README.md) | ![pypi version](https://img.shields.io/pypi/v/toolbox-adk.svg) | + + +## Getting Started + +To get started using Toolbox tools with an application, follow these general steps: + +1. **Set up and Run the Toolbox Service:** + + 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 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 + + # OR + + # For the LlamaIndex integration + pip install toolbox-llamaindex + + # OR + + # For the Google ADK Integration + pip install google-adk[toolbox] + ``` + + + +{{< notice note >}} +Source code for [python-sdk](https://github.com/googleapis/mcp-toolbox-sdk-python) +{{< /notice >}} diff --git a/docs/en/sdks/python-sdk/adk/index.md b/docs/en/sdks/python-sdk/adk/index.md new file mode 100644 index 000000000000..b931a4342406 --- /dev/null +++ b/docs/en/sdks/python-sdk/adk/index.md @@ -0,0 +1,251 @@ +--- +title: "ADK" +type: docs +weight: 8 +description: > + MCP Toolbox SDK for integrating functionalities of MCP Toolbox into your ADK apps. +--- + +## Overview + +The `toolbox-adk` package provides a Python interface to the MCP Toolbox service, enabling you to load and invoke tools from your own applications. + +## Installation + +```bash +pip install google-adk[toolbox] +``` + +## Usage + +The primary entry point is the `ToolboxToolset`, which loads tools from a remote Toolbox server and adapts them for use with ADK agents. + +{{< notice note>}} +This package contains the core implementation of the `ToolboxToolset`. The `ToolboxToolset` provided in the [`google-adk`](https://github.com/google/adk-python/blob/758d337c76d877e3174c35f06551cc9beb1def06/src/google/adk/tools/toolbox_toolset.py#L35) package is a shim that simply delegates all functionality to this implementation. +{{< /notice >}} + +```python +from google.adk.tools.toolbox_toolset import ToolboxToolset +from google.adk.agents import Agent + +# Create the Toolset +toolset = ToolboxToolset( + server_url="http://127.0.0.1:5000" +) + +# Use in your ADK Agent +agent = Agent(tools=[toolset]) +``` + +## Transport Protocols + +The SDK supports multiple transport protocols for communicating with the Toolbox server. By default, the client uses the latest supported version of the **Model Context Protocol (MCP)**. + +You can explicitly select a protocol using the `protocol` option during toolset initialization. This is useful if you need to use the native Toolbox HTTP protocol or pin the client to a specific legacy version of MCP. + +{{< notice note>}} +* **Native Toolbox Transport**: This uses the service's native **REST over HTTP** API. +* **MCP Transports**: These options use the **Model Context Protocol over HTTP**. +{{< /notice >}} + +### Supported Protocols + +| Constant | Description | +| :--- | :--- | +| `Protocol.MCP` | **(Default)** Alias for the default MCP version (currently `2025-06-18`). | +| `Protocol.TOOLBOX` | **DEPRECATED**: The native Toolbox HTTP protocol. Will be removed on March 4, 2026. | +| `Protocol.MCP_v20251125` | MCP Protocol version 2025-11-25. | +| `Protocol.MCP_v20250618` | MCP Protocol version 2025-06-18. | +| `Protocol.MCP_v20250326` | MCP Protocol version 2025-03-26. | +| `Protocol.MCP_v20241105` | MCP Protocol version 2024-11-05. | + +{{< notice note >}} +The **Native Toolbox Protocol** (`Protocol.TOOLBOX`) is deprecated and will be removed on **March 4, 2026**. +Please migrate to using the **MCP Protocol** (`Protocol.MCP`), which is the default. +{{< /notice >}} + +### Example + +If you wish to use the native Toolbox protocol: + +```python +from toolbox_adk import ToolboxToolset +from toolbox_core.protocol import Protocol + +toolset = ToolboxToolset( + server_url="http://127.0.0.1:5000", + protocol=Protocol.TOOLBOX +) +``` + +If you want to pin the MCP Version 2025-03-26: + +```python +from toolbox_adk import ToolboxToolset +from toolbox_core.protocol import Protocol + +toolset = ToolboxToolset( + server_url="http://127.0.0.1:5000", + protocol=Protocol.MCP_v20250326 +) +``` + +{{< notice tip>}} +By default, it uses **Toolbox Identity** (no authentication), which is suitable for local development. + +For production environments (Cloud Run, GKE) or accessing protected resources, see the [Authentication](#authentication) section for strategies like Workload Identity or OAuth2. +{{< /notice >}} + +## Authentication + +The `ToolboxToolset` requires credentials to authenticate with the Toolbox server. You can configure these credentials using the `CredentialStrategy` factory methods. + +The strategies handle two main types of authentication: +* **Client-to-Server**: Securing the connection to the Toolbox server (e.g., Workload Identity, API keys). +* **User Identity**: Authenticating the end-user for specific tools (e.g., 3-legged OAuth). + +### 1. Workload Identity (ADC) +*Recommended for Cloud Run, GKE, or local development with `gcloud auth login`.* + +Uses the agent's Application Default Credentials (ADC) to generate an OIDC token. This is the standard way for one service to authenticate to another on Google Cloud. + +```python +from toolbox_adk import CredentialStrategy, ToolboxToolset + +# target_audience: The URL of your Toolbox server +creds = CredentialStrategy.workload_identity(target_audience="https://my-toolbox-service.run.app") + +toolset = ToolboxToolset( + server_url="https://my-toolbox-service.run.app", + credentials=creds +) +``` + +### 2. User Identity (OAuth2) +*Recommended for tools that act on behalf of the user.* + +Configures the ADK-native interactive 3-legged OAuth flow to get consent and credentials from the end-user at runtime. This strategy is passed to the `ToolboxToolset` just like any other credential strategy. + +```python +from toolbox_adk import CredentialStrategy, ToolboxToolset + +creds = CredentialStrategy.user_identity( + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + scopes=["https://www.googleapis.com/auth/cloud-platform"] +) + +# The toolset will now initiate OAuth flows when required by tools +toolset = ToolboxToolset( + server_url="...", + credentials=creds +) +``` + +### 3. API Key +*Use a static API key passed in a specific header (default: `X-API-Key`).* + +```python +from toolbox_adk import CredentialStrategy + +# Default header: X-API-Key +creds = CredentialStrategy.api_key(key="my-secret-key") + +# Custom header +creds = CredentialStrategy.api_key(key="my-secret-key", header_name="X-My-Header") +``` + +### 4. HTTP Bearer Token +*Manually supply a static bearer token.* + +```python +from toolbox_adk import CredentialStrategy + +creds = CredentialStrategy.manual_token(token="your-static-bearer-token") +``` + +### 5. Manual Google Credentials +*Use an existing `google.auth.credentials.Credentials` object.* + +```python +from toolbox_adk import CredentialStrategy +import google.auth + +creds_obj, _ = google.auth.default() +creds = CredentialStrategy.manual_credentials(credentials=creds_obj) +``` + +### 6. Toolbox Identity (No Auth) +*Use this if your Toolbox server does not require authentication (e.g., local development).* + +```python +from toolbox_adk import CredentialStrategy + +creds = CredentialStrategy.toolbox_identity() +``` + +### 7. Native ADK Integration +*Convert ADK-native `AuthConfig` or `AuthCredential` objects.* + +```python +from toolbox_adk import CredentialStrategy + +# From AuthConfig +creds = CredentialStrategy.from_adk_auth_config(auth_config) + +# From AuthCredential + AuthScheme +creds = CredentialStrategy.from_adk_credentials(auth_credential, scheme) +``` + +### 8. Tool-Specific Authentication +*Resolve authentication tokens dynamically for specific tools.* + +Some tools may define their own authentication requirements (e.g., Salesforce OAuth, GitHub PAT) via `authSources` in their schema. You can provide a mapping of getters to resolve these tokens at runtime. + +```python +async def get_salesforce_token(): + # Fetch token from secret manager or reliable source + return "sf-access-token" + +toolset = ToolboxToolset( + server_url="...", + auth_token_getters={ + "salesforce-auth": get_salesforce_token, # Async callable + "github-pat": lambda: "my-pat-token" # Sync callable or static lambda + } +) +``` + +## Advanced Configuration + +### Additional Headers + +You can inject custom headers into every request made to the Toolbox server. This is useful for passing tracing IDs, API keys, or other metadata. + +```python +toolset = ToolboxToolset( + server_url="...", + additional_headers={ + "X-Trace-ID": "12345", + "X-My-Header": lambda: get_dynamic_header_value() # Can be a callable + } +) +``` + +### Parameter Binding + +Bind values to tool parameters globally across all loaded tools. These values will be **fixed** and **hidden** from the LLM. + +* **Schema Hiding**: The bound parameters are removed from the tool schema sent to the model, simplifying the context window. +* **Auto-Injection**: The values are automatically injected into the tool arguments during execution. + +```python +toolset = ToolboxToolset( + server_url="...", + bound_params={ + # 'region' will be hidden from the LLM and injected automatically + "region": "us-central1", + "api_key": lambda: get_api_key() # Can be a callable + } +) +``` diff --git a/docs/en/sdks/python-sdk/core/index.md b/docs/en/sdks/python-sdk/core/index.md new file mode 100644 index 000000000000..894ad88665fc --- /dev/null +++ b/docs/en/sdks/python-sdk/core/index.md @@ -0,0 +1,547 @@ +--- +title: "Core" +type: docs +weight: 8 +description: > + MCP Toolbox Core SDK for integrating functionalities of MCP Toolbox into your Agentic apps. +--- + +## Overview + +The `toolbox-core` package provides a Python interface to the MCP Toolbox service, enabling you to load and invoke tools from your own applications. + +## Installation + +```bash +pip install toolbox-core +``` + +{{< notice note >}} +* The primary `ToolboxClient` is asynchronous and requires using `await` for loading and invoking tools, as shown in most examples. +* Asynchronous code needs to run within an event loop (e.g., using `asyncio.run()` or in an async framework). See the [Python `asyncio` documentation](https://docs.python.org/3/library/asyncio-task.html) for more details. +* If you prefer synchronous execution, refer to the [Synchronous Usage](#synchronous-usage) section below. +{{< /notice >}} + +{{< notice note>}} +The `ToolboxClient` (and its synchronous counterpart `ToolboxSyncClient`) interacts with network resources using an underlying HTTP client session. You should remember to use a context manager or explicitly call `close()` to clean up these resources. If you provide your own session, you'll need to close it in addition to calling `ToolboxClient.close()`. +{{< /notice >}} + +## Quickstart + +1. **Start the Toolbox Service** + - Make sure the MCP Toolbox service is running on port `5000` of your local machine. See the [Toolbox Getting Started Guide](/getting-started/introduction/#getting-started). + +2. **Minimal Example** + +```python +import asyncio +from toolbox_core import ToolboxClient + +async def main(): + # Replace with the actual URL where your Toolbox service is running + async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + weather_tool = await toolbox.load_tool("get_weather") + result = await weather_tool(location="London") + print(result) + +if __name__ == "__main__": + asyncio.run(main()) +``` + + +{{< notice tip>}} +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) +{{< /notice >}} + +{{< notice note>}} +If you initialize `ToolboxClient` without providing an external session and cannot use `async with`, you must explicitly close the client using `await toolbox.close()` in a `finally` block. This ensures the internally created session is closed. + + ```py + toolbox = ToolboxClient("http://127.0.0.1:5000") + try: + # ... use toolbox ... + finally: + await toolbox.close() + ``` +{{< /notice >}} + +## Usage + +Import and initialize an MCP Toolbox client, pointing it to the URL of your running +Toolbox service. + +```py +from toolbox_core import ToolboxClient + +# Replace with your Toolbox service's URL +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: +``` + +All interactions for loading and invoking tools happen through this client. + +{{< notice tip>}} +For advanced use cases, you can provide an external `aiohttp.ClientSession` during initialization (e.g., `ToolboxClient(url, session=my_session)`). If you provide your own session, you are responsible for managing its lifecycle; `ToolboxClient` *will not* close it. +{{< /notice >}} + +{{< notice note>}} +Closing the `ToolboxClient` also closes the underlying network session shared by all tools loaded from that client. As a result, any tool instances you have loaded will cease to function and will raise an error if you attempt to invoke them after the client is closed. +{{< /notice >}} + +## Transport Protocols + +The SDK supports multiple transport protocols for communicating with the Toolbox server. By default, the client uses the latest supported version of the **Model Context Protocol (MCP)**. + +You can explicitly select a protocol using the `protocol` option during client initialization. This is useful if you need to use the native Toolbox HTTP protocol or pin the client to a specific legacy version of MCP. + +{{< notice note >}} +* **Native Toolbox Transport**: This uses the service's native **REST over HTTP** API. +* **MCP Transports**: These options use the **Model Context Protocol over HTTP**. +{{< /notice >}} + +### Supported Protocols + +| Constant | Description | +| :--- | :--- | +| `Protocol.MCP` | **(Default)** Alias for the default MCP version (currently `2025-06-18`). | +| `Protocol.TOOLBOX` | **DEPRECATED**: The native Toolbox HTTP protocol. Will be removed on March 4, 2026. | +| `Protocol.MCP_v20251125` | MCP Protocol version 2025-11-25. | +| `Protocol.MCP_v20250618` | MCP Protocol version 2025-06-18. | +| `Protocol.MCP_v20241105` | MCP Protocol version 2024-11-05. | + +{{< notice note >}} +The **Native Toolbox Protocol** (`Protocol.TOOLBOX`) is deprecated and will be removed on **March 4, 2026**. +Please migrate to using the **MCP Protocol** (`Protocol.MCP`), which is the default. +{{< /notice >}} + +### Example + +If you wish to use the native Toolbox protocol: + +```py +from toolbox_core import ToolboxClient +from toolbox_core.protocol import Protocol + +async with ToolboxClient("http://127.0.0.1:5000", protocol=Protocol.TOOLBOX) as toolbox: + # Use client + pass +``` + +If you want to pin the MCP Version 2025-03-26: + +```py +from toolbox_core import ToolboxClient +from toolbox_core.protocol import Protocol + +async with ToolboxClient("http://127.0.0.1:5000", protocol=Protocol.MCP_v20250326) as toolbox: + # Use client + pass +``` + +## Loading Tools + +You can load tools individually or in groups (toolsets) as defined in your +Toolbox service configuration. Loading a toolset is convenient when working with +multiple related functions, while loading a single tool offers more granular +control. + +### Load a toolset + +A toolset is a collection of related tools. You can load all tools in a toolset +or a specific one: + +```py +# Load all tools +tools = await toolbox.load_toolset() + +# Load a specific toolset +tools = await toolbox.load_toolset("my-toolset") +``` + +### Load a single tool + +Loads a specific tool by its unique name. This provides fine-grained control. + +```py +tool = await toolbox.load_tool("my-tool") +``` + +## Invoking Tools + +Once loaded, tools behave like awaitable Python functions. You invoke them using +`await` and pass arguments corresponding to the parameters defined in the tool's +configuration within the Toolbox service. + +```py +tool = await toolbox.load_tool("my-tool") +result = await tool("foo", bar="baz") +``` + +{{< notice tip>}} +For a more comprehensive guide on setting up the Toolbox service itself, which you'll need running to use this SDK, please refer to the [Toolbox Quickstart Guide](getting-started/local_quickstart). +{{< /notice >}} + +## Synchronous Usage + +By default, the `ToolboxClient` and the `ToolboxTool` objects it produces behave like asynchronous Python functions, requiring the use of `await`. + +If your application primarily uses synchronous code, or you prefer not to manage an asyncio event loop, you can use the synchronous alternatives provided: + +* `ToolboxSyncClient`: The synchronous counterpart to `ToolboxClient`. +* `ToolboxSyncTool`: The synchronous counterpart to `ToolboxTool`. + +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 + +with ToolboxSyncClient("http://127.0.0.1:5000") as toolbox: + weather_tool = toolbox.load_tool("get_weather") + result = weather_tool(location="Paris") + print(result) +``` + +{{< notice tip>}} +While synchronous invocation is available for convenience, it's generally considered best practice to use asynchronous operations (like those provided by the default `ToolboxClient` and `ToolboxTool`) for an I/O-bound task like tool invocation. Asynchronous programming allows for cooperative multitasking, often leading to better performance and resource utilization, especially in applications handling concurrent requests. +{{< /notice >}} + +## Use with LangGraph + +The Toolbox Core SDK integrates smoothly with frameworks like LangGraph, +allowing you to incorporate tools managed by the Toolbox service into your +agentic workflows. + +{{< notice tip>}} +The loaded tools (both async `ToolboxTool` and sync `ToolboxSyncTool`) are callable and can often be used directly. However, to ensure parameter descriptions from Google-style docstrings are accurately parsed and made available to the LLM (via `bind_tools()`) and LangGraph internals, it's recommended to wrap the loaded tools using LangChain's [`StructuredTool`](https://python.langchain.com/api_reference/core/tools/langchain_core.tools.structured.StructuredTool.html). +{{< /notice >}} + + +Here's a conceptual example adapting the [official LangGraph tool calling +guide](https://langchain-ai.github.io/langgraph/how-tos/tool-calling): + +```py +import asyncio +from typing import Annotated +from typing_extensions import TypedDict +from langchain_core.messages import HumanMessage, BaseMessage +from toolbox_core import ToolboxClient +from langchain_google_vertexai import ChatVertexAI +from langgraph.graph import StateGraph, START, END +from langgraph.prebuilt import ToolNode +from langchain.tools import StructuredTool +from langgraph.graph.message import add_messages + +class State(TypedDict): + messages: Annotated[list[BaseMessage], add_messages] + +async def main(): + async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tools = await toolbox.load_toolset() + wrapped_tools = [StructuredTool.from_function(tool, parse_docstring=True) for tool in tools] + model_with_tools = ChatVertexAI(model="gemini-3-flash-preview").bind_tools(wrapped_tools) + tool_node = ToolNode(wrapped_tools) + + def call_agent(state: State): + response = model_with_tools.invoke(state["messages"]) + return {"messages": [response]} + + def should_continue(state: State): + last_message = state["messages"][-1] + if last_message.tool_calls: + return "tools" + return END + + graph_builder = StateGraph(State) + + graph_builder.add_node("agent", call_agent) + graph_builder.add_node("tools", tool_node) + + graph_builder.add_edge(START, "agent") + graph_builder.add_conditional_edges( + "agent", + should_continue, + ) + graph_builder.add_edge("tools", "agent") + + app = graph_builder.compile() + + prompt = "What is the weather in London?" + inputs = {"messages": [HumanMessage(content=prompt)]} + + print(f"User: {prompt}\n") + print("--- Streaming Agent Steps ---") + + events = app.stream( + inputs, + stream_mode="values", + ) + + for event in events: + event["messages"][-1].pretty_print() + print("\n---\n") + +asyncio.run(main()) +``` + +## Client to Server Authentication + +This section describes how to authenticate the ToolboxClient itself when +connecting to an MCP Toolbox server instance that requires authentication. This is +crucial for securing your Toolbox server endpoint, especially when deployed on +platforms like Cloud Run, GKE, or any environment where unauthenticated access is restricted. + +This client-to-server authentication ensures that the Toolbox server can verify +the identity of the client making the request before any tool is loaded or +called. It is different from [Authenticating Tools](#authenticating-tools), +which deals with providing credentials for specific tools within an already +connected Toolbox session. + +### When is Client-to-Server Authentication Needed? + +You'll need this type of authentication if your Toolbox server is configured to +deny unauthenticated requests. For example: + +- Your Toolbox server is deployed on Cloud Run and configured to "Require authentication." +- Your server is behind an Identity-Aware Proxy (IAP) or a similar + authentication layer. +- You have custom authentication middleware on your self-hosted Toolbox server. + +Without proper client authentication in these scenarios, attempts to connect or +make calls (like `load_tool`) will likely fail with `Unauthorized` errors. + +### How it works + +The `ToolboxClient` (and `ToolboxSyncClient`) allows you to specify functions +(or coroutines for the async client) that dynamically generate HTTP headers for +every request sent to the Toolbox server. The most common use case is to add an +Authorization header with a bearer token (e.g., a Google ID token). + +These header-generating functions are called just before each request, ensuring +that fresh credentials or header values can be used. + +### Configuration + +You can configure these dynamic headers as seen below: + +```python +from toolbox_core import ToolboxClient + +async with ToolboxClient("toolbox-url", client_headers={"header1": header1_getter, "header2": header2_getter, ...}) as client: + # Use client + pass +``` + +### Authenticating with Google Cloud Servers + +For Toolbox servers hosted on Google Cloud (e.g., Cloud Run) and requiring +`Google ID token` authentication, the helper module +[auth_methods](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/src/toolbox_core/auth_methods.py) provides utility functions. + +### Step by Step Guide for Cloud Run + +1. **Configure Permissions**: [Grant](https://cloud.google.com/run/docs/securing/managing-access#service-add-principals) the `roles/run.invoker` IAM role on the Cloud + Run service to the principal. This could be your `user account email` or a + `service account`. +2. **Configure Credentials** + - Local Development: Set up + [ADC](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment). + - Google Cloud Environments: When running within Google Cloud (e.g., Compute + Engine, GKE, another Cloud Run service, Cloud Functions), ADC is typically + configured automatically, using the environment's default service account. +3. **Connect to the Toolbox Server** + + ```python + from toolbox_core import auth_methods + + auth_token_provider = auth_methods.aget_google_id_token(URL) # can also use sync method + async with ToolboxClient( + URL, + client_headers={"Authorization": auth_token_provider}, + ) as client: + tools = await client.load_toolset() + + # Now, you can use the client as usual. + ``` + +## Authenticating Tools + +{{< notice info >}} +**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, such as eavesdropping and tampering. +{{}} + +Tools can be configured within the Toolbox service to require authentication, +ensuring only authorized users or applications can invoke them, especially when +accessing sensitive data. + +### When is Authentication Needed? + +Authentication is configured per-tool within the Toolbox service itself. If a +tool you intend to use is marked as requiring authentication in the service, you +must configure the SDK client to provide the necessary credentials (currently +Oauth2 tokens) when invoking that specific tool. + +### Supported Authentication Mechanisms + +The Toolbox service enables secure tool usage through **Authenticated Parameters**. For detailed information on how these mechanisms work within the Toolbox service and how to configure them, please refer to [Authenticated Parameters](resources/tools/#authenticated-parameters) + +### Step 1: Configure Tools in Toolbox Service + +First, ensure the target tool(s) are configured correctly in the Toolbox service +to require authentication. Refer to the [Authenticated Parameters](https://googleapis.github.io/genai-toolbox/resources/tools/#authenticated-parameters) +for instructions. + +### Step 2: Configure SDK Client + +Your application needs a way to obtain the required Oauth2 token for the +authenticated user. The SDK requires you to provide a function capable of +retrieving this token *when the tool is invoked*. + +#### Provide an ID Token Retriever Function + +You must provide the SDK with a function (sync or async) that returns the +necessary token when called. The implementation depends on your application's +authentication flow (e.g., retrieving a stored token, initiating an OAuth flow). + +{{< notice info>}} +The name used when registering the getter function with the SDK (e.g.,`"my_api_token"`) must exactly match the `name` of the corresponding `authServices` defined in the tool's configuration within the Toolbox service. +{{}} + +```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 +``` + +{{< notice 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. +{{}} + +#### Option A: Add Authentication to a Loaded Tool + +You can add the token retriever function to a tool object *after* it has been +loaded. This modifies the specific tool instance. + +```py +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tool = await toolbox.load_tool("my-tool") + + auth_tool = tool.add_auth_token_getter("my_auth", get_auth_token) # Single token + + # OR + + multi_auth_tool = tool.add_auth_token_getters({ + "my_auth_1": get_auth_token_1, + "my_auth_2": get_auth_token_2, + }) # Multiple tokens +``` + +#### Option B: Add Authentication While Loading Tools + +You can provide the token retriever(s) directly during the `load_tool` or +`load_toolset` calls. This applies the authentication configuration only to the +tools loaded in that specific call, without modifying the original tool objects +if they were loaded previously. + +```py +auth_tool = await toolbox.load_tool(auth_token_getters={"my_auth": get_auth_token}) + +# OR + +auth_tools = await toolbox.load_toolset(auth_token_getters={"my_auth": get_auth_token}) +``` + +{{< notice >}} +Adding auth tokens during loading only affect the tools loaded within that call. +{{}} + +### Complete Authentication Example + +```py +import asyncio +from toolbox_core 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 + +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tool = await toolbox.load_tool("my-tool") + + auth_tool = tool.add_auth_token_getters({"my_auth": get_auth_token}) + result = auth_tool(input="some input") + print(result) +``` + +{{< notice >}} +An auth token getter for a specific name (e.g., `“GOOGLE_ID”`) will replace any client header with the same name followed by `“_token”` (e.g., `“GOOGLE_ID_token”`). +{{}} + + +## Parameter Binding + +The SDK allows you to pre-set, or "bind", values for specific tool parameters +before the tool is invoked or even passed to an LLM. These bound values are +fixed and will not be requested or modified by the LLM during tool use. + +### Why Bind Parameters? + +- **Protecting sensitive information:** API keys, secrets, etc. +- **Enforcing consistency:** Ensuring specific values for certain parameters. +- **Pre-filling known data:** Providing defaults or context. + +{{< notice info >}} +The parameter names used for binding (e.g., `"api_key"`) must exactly match the parameter names defined in the tool’s configuration within the Toolbox service. +{{}} + +{{< notice >}} +You do not need to modify the tool’s configuration in the Toolbox service to bind parameter values using the SDK. +{{}} + +### Option A: Binding Parameters to a Loaded Tool + +Bind values to a tool object *after* it has been loaded. This modifies the +specific tool instance. + +```py +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tool = await toolbox.load_tool("my-tool") + + bound_tool = tool.bind_param("param", "value") + + # OR + + bound_tool = tool.bind_params({"param": "value"}) +``` + +### Option B: Binding Parameters While Loading Tools + +Specify bound parameters directly when loading tools. This applies the binding +only to the tools loaded in that specific call. + +```py +bound_tool = await toolbox.load_tool("my-tool", bound_params={"param": "value"}) + +# OR + +bound_tools = await toolbox.load_toolset(bound_params={"param": "value"}) +``` + +### Binding Dynamic Values + +Instead of a static value, you can bind a parameter to a synchronous or +asynchronous function. This function will be called *each time* the tool is +invoked to dynamically determine the parameter's value at runtime. + +{{< notice >}} + You don’t need to modify tool configurations to bind parameter values. +{{}} + +```py +async def get_dynamic_value(): + # Logic to determine the value + return "dynamic_value" + +# Assuming `tool` is a loaded tool instance from a ToolboxClient +dynamic_bound_tool = tool.bind_param("param", get_dynamic_value) +``` diff --git a/docs/en/sdks/python-sdk/langchain/index.md b/docs/en/sdks/python-sdk/langchain/index.md new file mode 100644 index 000000000000..9acfff92c824 --- /dev/null +++ b/docs/en/sdks/python-sdk/langchain/index.md @@ -0,0 +1,452 @@ +--- +title: "LangChain/LangGraph" +type: docs +weight: 8 +description: > + MCP Toolbox SDK for integrating functionalities of MCP Toolbox into your LangChain/LangGraph apps. +--- + +## Overview + +The `toolbox-langchain` package provides a Python interface to the MCP Toolbox service, enabling you to load and invoke tools from your own applications. + +## Installation + +```bash +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): + +```py +from toolbox_langchain import ToolboxClient +from langchain_google_vertexai import ChatVertexAI +from langgraph.prebuilt import create_react_agent + +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tools = toolbox.load_toolset() + + model = ChatVertexAI(model="gemini-3-flash-preview") + agent = create_react_agent(model, tools) + + prompt = "How's the weather today?" + + 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() +``` +{{< notice tip >}} +For a complete, end-to-end example including setting up the service and using an SDK, see the full tutorial: [Toolbox Quickstart Tutorial](getting-started/local_quickstart) +{{< /notice >}} + +## Usage + +Import and initialize the toolbox client. + +```py +from toolbox_langchain import ToolboxClient + +# Replace with your Toolbox service's URL +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: +``` + +## Transport Protocols + +The SDK supports multiple transport protocols for communicating with the Toolbox server. By default, the client uses the latest supported version of the **Model Context Protocol (MCP)**. + +You can explicitly select a protocol using the `protocol` option during client initialization. This is useful if you need to use the native Toolbox HTTP protocol or pin the client to a specific legacy version of MCP. + +{{< notice note >}} +* **Native Toolbox Transport**: This uses the service's native **REST over HTTP** API. +* **MCP Transports**: These options use the **Model Context Protocol over HTTP**. +{{< /notice >}} + +### Supported Protocols + +| Constant | Description | +| :--- | :--- | +| `Protocol.MCP` | **(Default)** Alias for the default MCP version (currently `2025-06-18`). | +| `Protocol.TOOLBOX` | **DEPRECATED**: The native Toolbox HTTP protocol. Will be removed on March 4, 2026. | +| `Protocol.MCP_v20251125` | MCP Protocol version 2025-11-25. | +| `Protocol.MCP_v20250618` | MCP Protocol version 2025-06-18. | +| `Protocol.MCP_v20250326` | MCP Protocol version 2025-03-26. | +| `Protocol.MCP_v20241105` | MCP Protocol version 2024-11-05. | + +{{< notice note >}} +The **Native Toolbox Protocol** (`Protocol.TOOLBOX`) is deprecated and will be removed on **March 4, 2026**. +Please migrate to using the **MCP Protocol** (`Protocol.MCP`), which is the default. +{{< /notice >}} + +### Example + +If you wish to use the native Toolbox protocol: + +```py +from toolbox_langchain import ToolboxClient +from toolbox_core.protocol import Protocol + +async with ToolboxClient("http://127.0.0.1:5000", protocol=Protocol.TOOLBOX) as toolbox: + # Use client + pass +``` + +If you want to pin the MCP Version 2025-03-26: + +```py +from toolbox_langchain import ToolboxClient +from toolbox_core.protocol import Protocol + +async with ToolboxClient("http://127.0.0.1:5000", protocol=Protocol.MCP_v20250326) as toolbox: + # Use client + pass +``` + +## Loading Tools + +### Load a toolset + +A toolset is a collection of related tools. You can load all tools in a toolset +or a specific one: + +```py +# Load all tools +tools = toolbox.load_toolset() + +# Load a specific toolset +tools = toolbox.load_toolset("my-toolset") +``` + +### Load a single tool + +```py +tool = toolbox.load_tool("my-tool") +``` + +Loading individual tools gives you finer-grained control over which tools are +available to your LLM agent. + +## Use with LangChain + +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: + +```py +from langchain_google_vertexai import ChatVertexAI + +model = ChatVertexAI(model="gemini-3-flash-preview") + +# Initialize agent with tools +agent = model.bind_tools(tools) + +# Run the agent +result = agent.invoke("Do something with the tools") +``` + +## Use with LangGraph + +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. + +### Represent Tools as Nodes + +Represent each tool as a LangGraph node, encapsulating the tool's execution within the node's functionality: + +```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-3-flash-preview") +builder = StateGraph(MessagesState) +tool_node = ToolNode(tools) + +builder.add_node("agent", call_model) +builder.add_node("tools", tool_node) +``` + +### Connect Tools with LLM + +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: + +```py +from typing import Literal +from langgraph.graph import END, START +from langchain_core.messages import HumanMessage + +# 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 + +builder.add_edge(START, "agent") +builder.add_conditional_edges("agent", should_continue) +builder.add_edge("tools", 'agent') + +graph = builder.compile() + +graph.invoke({"messages": [HumanMessage(content="Do something with the tools")]}) +``` + +## Manual usage + +Execute a tool manually using the `invoke` method: + +```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. + +## Client to Server Authentication + +This section describes how to authenticate the ToolboxClient itself when +connecting to a Toolbox server instance that requires authentication. This is +crucial for securing your Toolbox server endpoint, especially when deployed on +platforms like Cloud Run, GKE, or any environment where unauthenticated access +is restricted. + +This client-to-server authentication ensures that the Toolbox server can verify +the identity of the client making the request before any tool is loaded or +called. It is different from [Authenticating Tools](#authenticating-tools), +which deals with providing credentials for specific tools within an already +connected Toolbox session. + +### When is Client-to-Server Authentication Needed? + +You'll need this type of authentication if your Toolbox server is configured to +deny unauthenticated requests. For example: + +- Your Toolbox server is deployed on Cloud Run and configured to "Require authentication." +- Your server is behind an Identity-Aware Proxy (IAP) or a similar + authentication layer. +- You have custom authentication middleware on your self-hosted Toolbox server. + +Without proper client authentication in these scenarios, attempts to connect or +make calls (like `load_tool`) will likely fail with `Unauthorized` errors. + +### How it works + +The `ToolboxClient` allows you to specify functions (or coroutines for the async +client) that dynamically generate HTTP headers for every request sent to the +Toolbox server. The most common use case is to add an Authorization header with +a bearer token (e.g., a Google ID token). + +These header-generating functions are called just before each request, ensuring +that fresh credentials or header values can be used. + +### Configuration + +You can configure these dynamic headers as follows: + +```python +from toolbox_langchain import ToolboxClient + +async with ToolboxClient( + "toolbox-url", + client_headers={"header1": header1_getter, "header2": header2_getter, ...} +) as client: +``` + +### Authenticating with Google Cloud Servers + +For Toolbox servers hosted on Google Cloud (e.g., Cloud Run) and requiring +`Google ID token` authentication, the helper module +[auth_methods](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/src/toolbox_core/auth_methods.py) provides utility functions. + +### Step by Step Guide for Cloud Run + +1. **Configure Permissions**: + [Grant](https://cloud.google.com/run/docs/securing/managing-access#service-add-principals) + the `roles/run.invoker` IAM role on the Cloud + Run service to the principal. This could be your `user account email` or a + `service account`. +2. **Configure Credentials** + - Local Development: Set up + [ADC](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment). + - Google Cloud Environments: When running within Google Cloud (e.g., Compute + Engine, GKE, another Cloud Run service, Cloud Functions), ADC is typically + configured automatically, using the environment's default service account. +3. **Connect to the Toolbox Server** + + ```python + from toolbox_langchain import ToolboxClient + from toolbox_core import auth_methods + + auth_token_provider = auth_methods.aget_google_id_token(URL) # can also use sync method + async with ToolboxClient( + URL, + client_headers={"Authorization": auth_token_provider}, + ) as client: + tools = client.load_toolset() + + # Now, you can use the client as usual. + ``` + + +## Authenticating Tools + +{{< notice info >}} +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. +{{< /notice >}} + +Some tools require user authentication to access sensitive data. + +### 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). + +### Configure Tools + +Refer to [these +instructions](https://googleapis.github.io/genai-toolbox/resources/tools/#authenticated-parameters) on +configuring tools for authenticated parameters. + +### Configure SDK + +You need a method to retrieve an ID token from your authentication service: + +```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 +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tools = toolbox.load_toolset() + + auth_tool = tools[0].add_auth_token_getter("my_auth", get_auth_token) # Single token + + multi_auth_tool = tools[0].add_auth_token_getters({"auth_1": get_auth_1}, {"auth_2": get_auth_2}) # Multiple tokens + + # OR + + auth_tools = [tool.add_auth_token_getter("my_auth", get_auth_token) for tool in tools] +``` + +#### Add Authentication While Loading + +```py +auth_tool = toolbox.load_tool(auth_token_getters={"my_auth": get_auth_token}) + +auth_tools = toolbox.load_toolset(auth_token_getters={"my_auth": get_auth_token}) +``` +{{< notice note >}} +Adding auth tokens during loading only affect the tools loaded within that call. +{{< /notice >}} + +### Complete Example + +```py +import asyncio +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 + +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tool = toolbox.load_tool("my-tool") + + auth_tool = tool.add_auth_token_getter("my_auth", get_auth_token) + result = auth_tool.invoke({"input": "some input"}) + print(result) +``` + +## Parameter Binding + +Predetermine values for tool parameters using the SDK. These values won't be +modified by the LLM. This is useful for: + +* **Protecting sensitive information:** API keys, secrets, etc. +* **Enforcing consistency:** Ensuring specific values for certain parameters. +* **Pre-filling known data:** Providing defaults or context. + +### Binding Parameters to a Tool + +```py +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tools = toolbox.load_toolset() + + bound_tool = tool[0].bind_param("param", "value") # Single param + + multi_bound_tool = tools[0].bind_params({"param1": "value1", "param2": "value2"}) # Multiple params + + # 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"}) +``` +{{< notice note >}} +Bound values during loading only affect the tools loaded in that call. +{{< /notice >}} + +### Binding Dynamic Values + +Use a function to bind dynamic values: + +```py +def get_dynamic_value(): + # Logic to determine the value + return "dynamic_value" + +dynamic_bound_tool = tool.bind_param("param", get_dynamic_value) +``` +{{< notice note >}} +You don’t need to modify tool configurations to bind parameter values. +{{< /notice >}} + +## Asynchronous Usage + +For better performance through [cooperative +multitasking](https://en.wikipedia.org/wiki/Cooperative_multitasking), you can +use the asynchronous interfaces of the `ToolboxClient`. + +{{< notice 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). +{{< /notice >}} + +```py +import asyncio +from toolbox_langchain import ToolboxClient + +async def main(): + async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tool = await client.aload_tool("my-tool") + tools = await client.aload_toolset() + response = await tool.ainvoke() + +if __name__ == "__main__": + asyncio.run(main()) +``` diff --git a/docs/en/sdks/python-sdk/llamaindex/index.md b/docs/en/sdks/python-sdk/llamaindex/index.md new file mode 100644 index 000000000000..14524bf79a4b --- /dev/null +++ b/docs/en/sdks/python-sdk/llamaindex/index.md @@ -0,0 +1,437 @@ +--- +title: "LlamaIndex" +type: docs +weight: 8 +description: > + MCP Toolbox LlamaIndex SDK for integrating functionalities of MCP Toolbox into your LlamaIndex apps. +--- + +## Overview + +The `toolbox-llamaindex` package provides a Python interface to the MCP Toolbox service, enabling you to load and invoke tools from your own applications. + +## Installation + +```bash +pip install toolbox-llamaindex +``` + +## Quickstart + +Here's a minimal example to get you started using +[LlamaIndex](https://docs.llamaindex.ai/en/stable/#getting-started): + +```py +import asyncio + +from llama_index.llms.google_genai import GoogleGenAI +from llama_index.core.agent.workflow import AgentWorkflow + +from toolbox_llamaindex import ToolboxClient + +async def run_agent(): + async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tools = toolbox.load_toolset() + + vertex_model = GoogleGenAI( + model="gemini-3-flash-preview", + vertexai_config={"project": "project-id", "location": "us-central1"}, + ) + agent = AgentWorkflow.from_tools_or_functions( + tools, + llm=vertex_model, + system_prompt="You are a helpful assistant.", + ) + response = await agent.run(user_msg="Get some response from the agent.") + print(response) + +asyncio.run(run_agent()) +``` + +{{< notice tip >}} +For a complete, end-to-end example including setting up the service and using an SDK, see the full tutorial: [Toolbox Quickstart Tutorial](getting-started/local_quickstart) +{{< /notice >}} + +## Usage + +Import and initialize the toolbox client. + +```py +from toolbox_llamaindex import ToolboxClient + +# Replace with your Toolbox service's URL +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: +``` + +## Transport Protocols + +The SDK supports multiple transport protocols for communicating with the Toolbox server. By default, the client uses the latest supported version of the **Model Context Protocol (MCP)**. + +You can explicitly select a protocol using the `protocol` option during client initialization. This is useful if you need to use the native Toolbox HTTP protocol or pin the client to a specific legacy version of MCP. + +{{< notice note >}} +* **Native Toolbox Transport**: This uses the service's native **REST over HTTP** API. +* **MCP Transports**: These options use the **Model Context Protocol over HTTP**. +{{< /notice >}} + +### Supported Protocols + +| Constant | Description | +| :--- | :--- | +| `Protocol.MCP` | **(Default)** Alias for the default MCP version (currently `2025-06-18`). | +| `Protocol.TOOLBOX` | **DEPRECATED**: The native Toolbox HTTP protocol. Will be removed on March 4, 2026. | +| `Protocol.MCP_v20251125` | MCP Protocol version 2025-11-25. | +| `Protocol.MCP_v20250618` | MCP Protocol version 2025-06-18. | +| `Protocol.MCP_v20250326` | MCP Protocol version 2025-03-26. | +| `Protocol.MCP_v20241105` | MCP Protocol version 2024-11-05. | + +{{< notice note >}} +The **Native Toolbox Protocol** (`Protocol.TOOLBOX`) is deprecated and will be removed on **March 4, 2026**. +Please migrate to using the **MCP Protocol** (`Protocol.MCP`), which is the default. +{{< /notice >}} + +### Example + +If you wish to use the native Toolbox protocol: + +```py +from toolbox_llamaindex import ToolboxClient +from toolbox_core.protocol import Protocol + +async with ToolboxClient("http://127.0.0.1:5000", protocol=Protocol.TOOLBOX) as toolbox: + # Use client + pass +``` + +If you want to pin the MCP Version 2025-03-26: + +```py +from toolbox_llamaindex import ToolboxClient +from toolbox_core.protocol import Protocol + +async with ToolboxClient("http://127.0.0.1:5000", protocol=Protocol.MCP_v20250326) as toolbox: + # Use client + pass +``` + +## Loading Tools + +### Load a toolset + +A toolset is a collection of related tools. You can load all tools in a toolset +or a specific one: + +```py +# Load all tools +tools = toolbox.load_toolset() + +# Load a specific toolset +tools = toolbox.load_toolset("my-toolset") +``` + +### Load a single tool + +```py +tool = toolbox.load_tool("my-tool") +``` + +Loading individual tools gives you finer-grained control over which tools are +available to your LLM agent. + +## Use with LlamaIndex + +LlamaIndex'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: + +```py +from llama_index.llms.google_genai import GoogleGenAI +from llama_index.core.agent.workflow import AgentWorkflow + +vertex_model = GoogleGenAI( + model="gemini-3-flash-preview", + vertexai_config={"project": "project-id", "location": "us-central1"}, +) + +# Initialize agent with tools +agent = AgentWorkflow.from_tools_or_functions( + tools, + llm=vertex_model, + system_prompt="You are a helpful assistant.", +) + +# Query the agent +response = await agent.run(user_msg="Get some response from the agent.") +print(response) +``` + +### Maintain state + +To maintain state for the agent, add context as follows: + +```py +from llama_index.core.agent.workflow import AgentWorkflow +from llama_index.core.workflow import Context +from llama_index.llms.google_genai import GoogleGenAI + +vertex_model = GoogleGenAI( + model="gemini-3-flash-preview", + vertexai_config={"project": "project-id", "location": "us-central1"}, +) +agent = AgentWorkflow.from_tools_or_functions( + tools, + llm=vertex_model, + system_prompt="You are a helpful assistant.", +) + +# Save memory in agent context +ctx = Context(agent) +response = await agent.run(user_msg="Give me some response.", ctx=ctx) +print(response) +``` + +## Manual usage + +Execute a tool manually using the `call` method: + +```py +result = tools[0].call(name="Alice", age=30) +``` + +This is useful for testing tools or when you need precise control over tool +execution outside of an agent framework. + +## Client to Server Authentication + +This section describes how to authenticate the ToolboxClient itself when +connecting to a Toolbox server instance that requires authentication. This is +crucial for securing your Toolbox server endpoint, especially when deployed on +platforms like Cloud Run, GKE, or any environment where unauthenticated access is restricted. + +This client-to-server authentication ensures that the Toolbox server can verify +the identity of the client making the request before any tool is loaded or +called. It is different from [Authenticating Tools](#authenticating-tools), +which deals with providing credentials for specific tools within an already +connected Toolbox session. + +### When is Client-to-Server Authentication Needed? + +You'll need this type of authentication if your Toolbox server is configured to +deny unauthenticated requests. For example: + +- Your Toolbox server is deployed on Cloud Run and configured to "Require authentication." +- Your server is behind an Identity-Aware Proxy (IAP) or a similar + authentication layer. +- You have custom authentication middleware on your self-hosted Toolbox server. + +Without proper client authentication in these scenarios, attempts to connect or +make calls (like `load_tool`) will likely fail with `Unauthorized` errors. + +### How it works + +The `ToolboxClient` allows you to specify functions (or coroutines for the async +client) that dynamically generate HTTP headers for every request sent to the +Toolbox server. The most common use case is to add an Authorization header with +a bearer token (e.g., a Google ID token). + +These header-generating functions are called just before each request, ensuring +that fresh credentials or header values can be used. + +### Configuration + +You can configure these dynamic headers as follows: + +```python +from toolbox_llamaindex import ToolboxClient + +async with ToolboxClient( + "toolbox-url", + client_headers={"header1": header1_getter, "header2": header2_getter}, +) as client: +``` + +### Authenticating with Google Cloud Servers + +For Toolbox servers hosted on Google Cloud (e.g., Cloud Run) and requiring +`Google ID token` authentication, the helper module +[auth_methods](https://github.com/googleapis/mcp-toolbox-sdk-python/blob/main/packages/toolbox-core/src/toolbox_core/auth_methods.py) provides utility functions. + +### Step by Step Guide for Cloud Run + +1. **Configure Permissions**: [Grant](https://cloud.google.com/run/docs/securing/managing-access#service-add-principals) the `roles/run.invoker` IAM role on the Cloud + Run service to the principal. This could be your `user account email` or a + `service account`. +2. **Configure Credentials** + - Local Development: Set up + [ADC](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment). + - Google Cloud Environments: When running within Google Cloud (e.g., Compute + Engine, GKE, another Cloud Run service, Cloud Functions), ADC is typically + configured automatically, using the environment's default service account. +3. **Connect to the Toolbox Server** + + ```python + from toolbox_llamaindex import ToolboxClient + from toolbox_core import auth_methods + + auth_token_provider = auth_methods.aget_google_id_token(URL) + async with ToolboxClient( + URL, + client_headers={"Authorization": auth_token_provider}, + ) as client: + tools = await client.aload_toolset() + + # Now, you can use the client as usual. + ``` + +## Authenticating Tools + +{{< notice info >}} +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. +{{< /notice >}} + +Some tools require user authentication to access sensitive data. + +### 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). + +### Configure Tools + +Refer to [these +instructions](https://googleapis.github.io/genai-toolbox/resources/tools/#authenticated-parameters) on +configuring tools for authenticated parameters. + +### Configure SDK + +You need a method to retrieve an ID token from your authentication service: + +```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 +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tools = toolbox.load_toolset() + + auth_tool = tools[0].add_auth_token_getter("my_auth", get_auth_token) # Single token + + multi_auth_tool = tools[0].add_auth_token_getters({"auth_1": get_auth_1}, {"auth_2": get_auth_2}) # Multiple tokens + + # OR + + auth_tools = [tool.add_auth_token_getter("my_auth", get_auth_token) for tool in tools] +``` + +#### Add Authentication While Loading + +```py +auth_tool = toolbox.load_tool(auth_token_getters={"my_auth": get_auth_token}) + +auth_tools = toolbox.load_toolset(auth_token_getters={"my_auth": get_auth_token}) +``` + +{{< notice note >}} +Adding auth tokens during loading only affect the tools loaded within that call. +{{< /notice >}} + +### Complete Example + +```py +import asyncio +from toolbox_llamaindex 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 + +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tool = toolbox.load_tool("my-tool") + + auth_tool = tool.add_auth_token_getter("my_auth", get_auth_token) + result = auth_tool.call(input="some input") + print(result) +``` + +## Parameter Binding + +Predetermine values for tool parameters using the SDK. These values won't be +modified by the LLM. This is useful for: + +* **Protecting sensitive information:** API keys, secrets, etc. +* **Enforcing consistency:** Ensuring specific values for certain parameters. +* **Pre-filling known data:** Providing defaults or context. + +### Binding Parameters to a Tool + +```py +async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tools = toolbox.load_toolset() + + bound_tool = tool[0].bind_param("param", "value") # Single param + + multi_bound_tool = tools[0].bind_params({"param1": "value1", "param2": "value2"}) # Multiple params + + # 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"}) +``` + +{{< notice note >}} +Bound values during loading only affect the tools loaded in that call. +{{< /notice >}} + +### Binding Dynamic Values + +Use a function to bind dynamic values: + +```py +def get_dynamic_value(): + # Logic to determine the value + return "dynamic_value" + +dynamic_bound_tool = tool.bind_param("param", get_dynamic_value) +``` + +{{< notice note >}} +You don't need to modify tool configurations to bind parameter values. +{{< /notice >}} + +## Asynchronous Usage + +For better performance through [cooperative +multitasking](https://en.wikipedia.org/wiki/Cooperative_multitasking), you can +use the asynchronous interfaces of the `ToolboxClient`. + +{{< notice 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). +{{< /notice >}} + +```py +import asyncio +from toolbox_llamaindex import ToolboxClient + +async def main(): + async with ToolboxClient("http://127.0.0.1:5000") as toolbox: + tool = await client.aload_tool("my-tool") + tools = await client.aload_toolset() + response = await tool.ainvoke() + +if __name__ == "__main__": + asyncio.run(main()) +```