Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
e5ab448624 chore(deps): bump ajv in /docs/en/getting-started/quickstart/js/genkit
Bumps [ajv](https://github.com/ajv-validator/ajv) from 8.17.1 to 8.18.0.
- [Release notes](https://github.com/ajv-validator/ajv/releases)
- [Commits](https://github.com/ajv-validator/ajv/compare/v8.17.1...v8.18.0)

---
updated-dependencies:
- dependency-name: ajv
  dependency-version: 8.18.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 19:03:54 +00:00
25 changed files with 112 additions and 2821 deletions

View File

@@ -1,57 +0,0 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
steps:
- name: "${_IMAGE}"
id: "go-pre-post-processing-test"
entrypoint: "bash"
args:
- -c
- |
set -ex
chmod +x .ci/sample_tests/run_tests.sh
.ci/sample_tests/run_tests.sh
env:
- "CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}"
- "GCP_PROJECT=${_GCP_PROJECT}"
- "DATABASE_NAME=${_DATABASE_NAME}"
- "DB_USER=${_DB_USER}"
- "TARGET_ROOT=${_TARGET_ROOT}"
- "TARGET_LANG=${_TARGET_LANG}"
- "TABLE_NAME=${_TABLE_NAME}"
- "SQL_FILE=${_SQL_FILE}"
- "AGENT_FILE_PATTERN=${_AGENT_FILE_PATTERN}"
secretEnv: ["TOOLS_YAML_CONTENT", "GOOGLE_API_KEY", "DB_PASSWORD"]
availableSecrets:
secretManager:
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/5
env: "TOOLS_YAML_CONTENT"
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
env: "GOOGLE_API_KEY"
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
env: "DB_PASSWORD"
timeout: 1200s
substitutions:
_TARGET_LANG: "go"
_IMAGE: "golang:1.25.1"
_TARGET_ROOT: "docs/en/samples/pre_post_processing/go"
_TABLE_NAME: "hotels_go_pre_post_processing"
_SQL_FILE: ".ci/sample_tests/setup_hotels.sql"
_AGENT_FILE_PATTERN: "agent.go"
options:
logging: CLOUD_LOGGING_ONLY

View File

@@ -1,57 +0,0 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
steps:
- name: "${_IMAGE}"
id: "js-pre-post-processing-test"
entrypoint: "bash"
args:
- -c
- |
set -ex
chmod +x .ci/sample_tests/run_tests.sh
.ci/sample_tests/run_tests.sh
env:
- "CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}"
- "GCP_PROJECT=${_GCP_PROJECT}"
- "DATABASE_NAME=${_DATABASE_NAME}"
- "DB_USER=${_DB_USER}"
- "TARGET_ROOT=${_TARGET_ROOT}"
- "TARGET_LANG=${_TARGET_LANG}"
- "TABLE_NAME=${_TABLE_NAME}"
- "SQL_FILE=${_SQL_FILE}"
- "AGENT_FILE_PATTERN=${_AGENT_FILE_PATTERN}"
secretEnv: ["TOOLS_YAML_CONTENT", "GOOGLE_API_KEY", "DB_PASSWORD"]
availableSecrets:
secretManager:
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/5
env: "TOOLS_YAML_CONTENT"
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
env: "GOOGLE_API_KEY"
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
env: "DB_PASSWORD"
timeout: 1200s
substitutions:
_TARGET_LANG: "js"
_IMAGE: "node:22"
_TARGET_ROOT: "docs/en/samples/pre_post_processing/js"
_TABLE_NAME: "hotels_js_pre_post_processing"
_SQL_FILE: ".ci/sample_tests/setup_hotels.sql"
_AGENT_FILE_PATTERN: "agent.js"
options:
logging: CLOUD_LOGGING_ONLY

View File

@@ -35,9 +35,7 @@ jobs:
ref: ${{ github.event.release.tag_name }}
- name: Get Version from Release Tag
run: echo "VERSION=${GITHUB_EVENT_RELEASE_TAG_NAME}" >> $GITHUB_ENV
env:
GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
run: echo "VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
- name: Setup Hugo
uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3

View File

@@ -1,132 +0,0 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Link Checker
on:
pull_request:
permissions:
contents: read
pull-requests: write
issues: write
jobs:
link-check:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Identify Changed Files
id: changed-files
shell: bash
run: |
git fetch origin main
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRT origin/main...HEAD -- '*.md')
if [ -z "$CHANGED_FILES" ]; then
echo "No markdown files changed. Skipping checks."
echo "HAS_CHANGES=false" >> $GITHUB_ENV
else
echo "--- Changed Files to Scan ---"
echo "$CHANGED_FILES"
echo "-----------------------------"
# FIX: Wrap filenames in quotes to handle spaces
FILES_QUOTED=$(echo "$CHANGED_FILES" | sed 's/^/"/;s/$/"/' | tr '\n' ' ')
# Write to env using EOF pattern
echo "CHECK_FILES<<EOF" >> $GITHUB_ENV
echo "$FILES_QUOTED" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "HAS_CHANGES=true" >> $GITHUB_ENV
fi
- name: Restore lychee cache
if: env.HAS_CHANGES == 'true'
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}
restore-keys: cache-lychee-
- name: Link Checker
id: lychee-check
if: env.HAS_CHANGES == 'true'
uses: lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0 # v2
continue-on-error: true
with:
args: >
--quiet
--no-progress
--cache
--max-cache-age 1d
--exclude '^neo4j\+.*' --exclude '^bolt://.*'
${{ env.CHECK_FILES }}
output: lychee-report.md
format: markdown
fail: true
jobSummary: false
debug: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Find comment
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: "## Link Resolution Note"
- name: Delete comment on success
if: steps.lychee-check.outcome == 'success' && steps.find-comment.outputs.comment-id != ''
run: |
gh api \
--method DELETE \
-H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/issues/comments/${{ steps.find-comment.outputs.comment-id }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare Report
if: env.HAS_CHANGES == 'true' && steps.lychee-check.outcome == 'failure'
run: |
echo "## Link Resolution Note" > full-report.md
echo "Local links and directory changes work differently on GitHub than on the docsite.You must ensure fixes pass the **GitHub check** and also work with **\`hugo server\`**." >> full-report.md
echo "See [Link Checking and Fixing with Lychee](https://github.com/googleapis/genai-toolbox/blob/main/DEVELOPER.md#link-checking-and-fixing-with-lychee) for more details." >> full-report.md
echo "" >> full-report.md
sed -E '/(Redirect|Redirects per input)/d' lychee-report.md >> full-report.md
- name: Create PR Comment
if: env.HAS_CHANGES == 'true' && steps.lychee-check.outcome == 'failure'
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body-path: full-report.md
edit-mode: replace
- name: Display Failure Report
# Run this ONLY if the link checker failed
if: steps.lychee-check.outcome == 'failure'
run: |
# We can now simply output the prepared file to the job summary
cat full-report.md >> $GITHUB_STEP_SUMMARY
# Fail the job
exit 1

View File

@@ -0,0 +1,68 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Link Checker
on:
pull_request:
jobs:
link-check:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Restore lychee cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}
restore-keys: cache-lychee-
- name: Link Checker
id: lychee-check
uses: lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0 # v2
continue-on-error: true
with:
args: >
--quiet
--no-progress
--cache
--max-cache-age 1d
--exclude '^neo4j\+.*' --exclude '^bolt://.*'
README.md
docs/
output: lychee-report.md
format: markdown
fail: true
jobSummary: false
debug: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Display Failure Report
# Run this ONLY if the link checker failed
if: steps.lychee-check.outcome == 'failure'
run: |
echo "## Link Resolution Note" >> $GITHUB_STEP_SUMMARY
echo "Local links and directory changes work differently on GitHub than on the docsite." >> $GITHUB_STEP_SUMMARY
echo "You must ensure fixes pass the **GitHub check** and also work with **\`hugo server\`**." >> $GITHUB_STEP_SUMMARY
echo "See [Link Checking and Fixing with Lychee](https://github.com/googleapis/genai-toolbox/blob/main/DEVELOPER.md#link-checking-and-fixing-with-lychee) for more details." >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "### Broken Links Found" >> $GITHUB_STEP_SUMMARY
cat ./lychee-report.md >> $GITHUB_STEP_SUMMARY
exit 1

View File

@@ -41,7 +41,7 @@
"# Getting Started With MCP Toolbox\n",
"\n",
"This guide demonstrates how to quickly run\n",
"[MCP Toolbox](https://github.com/googleapis/genai-toolbox) end-to-end in Google\n",
"[Toolbox](https://github.com/googleapis/genai-toolbox) end-to-end in Google\n",
"Colab using Python, PostgreSQL, and either [Google\n",
"GenAI](https://pypi.org/project/google-genai/), [ADK](https://google.github.io/adk-docs/),\n",
"[Langgraph](https://www.langchain.com/langgraph)\n",
@@ -49,12 +49,12 @@
"\n",
"Within this Colab environment, you'll\n",
"- Set up a `PostgreSQL database`.\n",
"- Launch an MCP Toolbox server.\n",
"- Connect to MCP Toolbox and develop a sample `Hotel Booking` application.\n",
"- Launch a Toolbox server.\n",
"- Connect to Toolbox and develop a sample `Hotel Booking` application.\n",
"\n",
"Here is the simplified flow of a MCP Toolbox Application:\n",
"Here is the simplified flow of a Toolbox Application:\n",
"\n",
"<img src=\"https://services.google.com/fh/files/misc/toolbox_flow.png\" alt=\"MCP Toolbox Flow\"/>\n",
"<img src=\"https://services.google.com/fh/files/misc/toolbox_flow.png\" alt=\"Toolbox Flow\"/>\n",
"\n"
]
},
@@ -208,12 +208,12 @@
"id": "EPuheP8DIt3p"
},
"source": [
"## Step 2: Install and configure MCP Toolbox\n",
"## Step 2: Install and configure Toolbox\n",
"\n",
"In this section, we will\n",
"1. Download the latest version of the MCP toolbox binary.\n",
"2. Create an MCP toolbox config file.\n",
"3. Start an MCP toolbox server using the config file.\n",
"1. Download the latest version of the toolbox binary.\n",
"2. Create a toolbox config file.\n",
"3. Start a toolbox server using the config file.\n",
"\n"
]
},
@@ -223,7 +223,7 @@
"id": "Bl1IeaqZbMYh"
},
"source": [
"Download the [latest](https://github.com/googleapis/genai-toolbox/releases) version of MCP Toolbox as a binary."
"Download the [latest](https://github.com/googleapis/genai-toolbox/releases) version of Toolbox as a binary."
]
},
{
@@ -284,7 +284,7 @@
"\n",
"Our application will leverage these tools to interact with the hotels database.\n",
"\n",
"For detailed configuration options, please refer to the [MCP Toolbox documentation](https://googleapis.github.io/genai-toolbox/getting-started/configure/).\n",
"For detailed configuration options, please refer to the [Toolbox documentation](https://googleapis.github.io/genai-toolbox/getting-started/configure/).\n",
"\n"
]
},
@@ -297,7 +297,7 @@
"outputs": [],
"source": [
"# Create a tools file at runtime.\n",
"# You can also upload a tools file and use that to run MCP toolbox.\n",
"# You can also upload a tools file and use that to run toolbox.\n",
"tools_file_name = \"tools.yml\"\n",
"file_content = f\"\"\"\n",
"kind: sources\n",
@@ -417,7 +417,7 @@
},
"outputs": [],
"source": [
"# Start an MCP toolbox server\n",
"# Start a toolbox server\n",
"! nohup {TOOLBOX_BINARY_PATH} --tools-file {TOOLS_FILE_PATH} -p {SERVER_PORT} > toolbox.log 2>&1 &"
]
},
@@ -429,7 +429,7 @@
},
"outputs": [],
"source": [
"# Check if MCP toolbox is running\n",
"# Check if toolbox is running\n",
"!sudo lsof -i :{SERVER_PORT}"
]
},
@@ -439,10 +439,10 @@
"id": "4yFH4JK7JEAv"
},
"source": [
"## Step 3: Connect your agent to MCP Toolbox\n",
"## Step 3: Connect your agent to Toolbox\n",
"\n",
"In this section, you will\n",
"1. Establish a connection to the tools by creating an MCP Toolbox client.\n",
"1. Establish a connection to the tools by creating a Toolbox client.\n",
"2. Build an agent that leverages the tools and an LLM for Hotel Booking functionality.\n"
]
},
@@ -495,7 +495,7 @@
"id": "J46eLkFbNhWq"
},
"source": [
"> You can either use LangGraph or LlamaIndex to develop an MCP Toolbox based\n",
"> You can either use LangGraph or LlamaIndex to develop a Toolbox based\n",
"> application. Run one of the sections below\n",
"> - [Connect using Google GenAI](#scrollTo=Fv2-uT4mvYtp)\n",
"> - [Connect using ADK](#scrollTo=QqRlWqvYNKSo)\n",
@@ -618,7 +618,7 @@
},
"outputs": [],
"source": [
"# Install the MCP Toolbox Langchain package\n",
"# Install the Toolbox Langchain package\n",
"!pip install toolbox-langchain --quiet\n",
"!pip install langgraph --quiet\n",
"\n",
@@ -679,7 +679,7 @@
" # model = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash-001\")\n",
" # model = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n",
"\n",
" # Load the tools from the MCP Toolbox server\n",
" # Load the tools from the Toolbox server\n",
" client = ToolboxClient(\"http://127.0.0.1:5000\")\n",
" tools = await client.aload_toolset()\n",
"\n",
@@ -711,7 +711,7 @@
},
"outputs": [],
"source": [
"# Install the MCP Toolbox LlamaIndex package\n",
"# Install the Toolbox LlamaIndex package\n",
"!pip install toolbox-llamaindex --quiet\n",
"\n",
"# Install the llamaindex llm package\n",
@@ -783,7 +783,7 @@
" # api_key=os.getenv(\"ANTHROPIC_API_KEY\")\n",
" # )\n",
"\n",
" # Load the tools from the MCP Toolbox server\n",
" # Load the tools from the Toolbox server\n",
" client = ToolboxClient(\"http://127.0.0.1:5000\")\n",
" tools = await client.aload_toolset()\n",
"\n",
@@ -821,7 +821,7 @@
},
"outputs": [],
"source": [
"# Install the MCP Toolbox Core package\n",
"# Install the Toolbox Core package\n",
"!pip install toolbox-core --quiet\n",
"\n",
"# Install the Google GenAI package\n",
@@ -999,7 +999,7 @@
"id": "yatf9YoGclV9"
},
"source": [
"Executing this will terminate the processes running on the database and MCP Toolbox ports.\n",
"Executing this will terminate the processes running on the database and Toolbox ports.\n",
"\n",
"This is necessary before re-running the startup cells for these services to prevent `port already in use` errors."
]

View File

@@ -3,7 +3,7 @@ title: "Python Quickstart (Local)"
type: docs
weight: 2
description: >
How to get started running MCP Toolbox locally with [Python](https://github.com/googleapis/mcp-toolbox-sdk-python), PostgreSQL, and [Agent Development Kit](https://google.github.io/adk-docs/),
How to get started running Toolbox locally with [Python](https://github.com/googleapis/mcp-toolbox-sdk-python), PostgreSQL, and [Agent Development Kit](https://google.github.io/adk-docs/),
[LangGraph](https://www.langchain.com/langgraph), [LlamaIndex](https://www.llamaindex.ai/) or [GoogleGenAI](https://pypi.org/project/google-genai/).
---
@@ -32,14 +32,14 @@ This guide assumes you have already done the following:
{{< regionInclude "quickstart/shared/database_setup.md" "database_setup" >}}
## Step 2: Install and configure MCP Toolbox
## Step 2: Install and configure Toolbox
{{< regionInclude "quickstart/shared/configure_toolbox.md" "configure_toolbox" >}}
## Step 3: Connect your agent to MCP Toolbox
## Step 3: Connect your agent to Toolbox
In this section, we will write and run an agent that will load the Tools
from MCP Toolbox.
from Toolbox.
{{< notice tip>}}
If you prefer to experiment within a Google Colab environment, you can connect
@@ -113,7 +113,7 @@ pip install google-genai
```
<br/>
1. Update `my_agent/agent.py` with the following content to connect to MCP Toolbox:
1. Update `my_agent/agent.py` with the following content to connect to Toolbox:
```py
{{< regionInclude "quickstart/python/adk/quickstart.py" "quickstart" >}}
```

View File

@@ -3,7 +3,7 @@ title: "Go Quickstart (Local)"
type: docs
weight: 4
description: >
How to get started running MCP Toolbox locally with [Go](https://github.com/googleapis/mcp-toolbox-sdk-go), PostgreSQL, and orchestration frameworks such as [LangChain Go](https://tmc.github.io/langchaingo/docs/), [GenkitGo](https://genkit.dev/go/docs/get-started-go/), [Go GenAI](https://github.com/googleapis/go-genai) and [OpenAI Go](https://github.com/openai/openai-go).
How to get started running Toolbox locally with [Go](https://github.com/googleapis/mcp-toolbox-sdk-go), PostgreSQL, and orchestration frameworks such as [LangChain Go](https://tmc.github.io/langchaingo/docs/), [GenkitGo](https://genkit.dev/go/docs/get-started-go/), [Go GenAI](https://github.com/googleapis/go-genai) and [OpenAI Go](https://github.com/openai/openai-go).
---
## Before you begin
@@ -24,14 +24,14 @@ This guide assumes you have already done the following:
{{< regionInclude "quickstart/shared/database_setup.md" "database_setup" >}}
## Step 2: Install and configure MCP Toolbox
## Step 2: Install and configure Toolbox
{{< regionInclude "quickstart/shared/configure_toolbox.md" "configure_toolbox" >}}
## Step 3: Connect your agent to MCP Toolbox
## Step 3: Connect your agent to Toolbox
In this section, we will write and run an agent that will load the Tools
from MCP Toolbox.
from Toolbox.
1. Initialize a go module:

View File

@@ -3,7 +3,7 @@ title: "JS Quickstart (Local)"
type: docs
weight: 3
description: >
How to get started running MCP Toolbox locally with [JavaScript](https://github.com/googleapis/mcp-toolbox-sdk-js), PostgreSQL, and orchestration frameworks such as [LangChain](https://js.langchain.com/docs/introduction/), [GenkitJS](https://genkit.dev/docs/get-started/), [LlamaIndex](https://ts.llamaindex.ai/) and [GoogleGenAI](https://github.com/googleapis/js-genai).
How to get started running Toolbox locally with [JavaScript](https://github.com/googleapis/mcp-toolbox-sdk-js), PostgreSQL, and orchestration frameworks such as [LangChain](https://js.langchain.com/docs/introduction/), [GenkitJS](https://genkit.dev/docs/get-started/), [LlamaIndex](https://ts.llamaindex.ai/) and [GoogleGenAI](https://github.com/googleapis/js-genai).
---
## Before you begin
@@ -24,14 +24,14 @@ This guide assumes you have already done the following:
{{< regionInclude "quickstart/shared/database_setup.md" "database_setup" >}}
## Step 2: Install and configure MCP Toolbox
## Step 2: Install and configure Toolbox
{{< regionInclude "quickstart/shared/configure_toolbox.md" "configure_toolbox" >}}
## Step 3: Connect your agent to MCP Toolbox
## Step 3: Connect your agent to Toolbox
In this section, we will write and run an agent that will load the Tools
from MCP Toolbox.
from Toolbox.
1. (Optional) Initialize a Node.js project:

View File

@@ -1,5 +1,3 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -11,7 +9,6 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (

View File

@@ -3231,9 +3231,10 @@
}
},
"node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",

View File

@@ -50,30 +50,5 @@ It is helpful to understand how tool-level processing differs from other scopes:
- **Model Level**: Intercepts individual calls to the LLM (prompts and responses). Unlike tool-level, this applies globally to all text sent/received, making it better for global PII redaction or token tracking.
- **Agent Level**: Wraps the high-level execution loop (e.g., a "turn" in the conversation). Unlike tool-level, this envelopes the entire turn (user input to final response), making it suitable for session management or end-to-end auditing.
## Best Practices
### Security & Guardrails
- **Principle of Least Privilege**: Ensure that tools run with the minimum necessary permissions. Middleware is an excellent place to enforce "read-only" modes or verify user identity before executing sensitive actions.
- **Input Sanitization**: Actively strip potential PII (like credit card numbers or raw emails) from tool arguments before logging them.
- **Prompt Injection Defense**: Use pre-processing hooks to scan user inputs for known jailbreak patterns or malicious directives before they reach the model or tools.
### Observability & Debugging
- **Structured Logging**: Instead of simple print statements, use structured JSON logging with correlation IDs. This allows you to trace a single user request through multiple agent turns and tool calls.
- **Logging for Testability**: LLM responses are non-deterministic and may summarize away key details.
- **Pattern**: Add explicit logging markers in your post-processing middleware (e.g., `logger.info("ACTION_SUCCESS: <id>")`).
- **Benefit**: Your integration tests can grep logs for these stable markers to verify tool success, rather than painfully parsing variable natural language responses.
### Performance & Cost Optimization
- **Token Economy**: Tools often return verbose JSON. Use post-processing to strip unnecessary fields or summarize large datasets *before* returning the result to the LLM's context window. This saves tokens and reduces latency.
- **Caching**: For read-heavy tools (like "search_knowledge_base"), implement caching middleware to return previous results for identical queries, saving both time and API costs.
### Error Handling
- **Graceful Degradation**: If a tool fails (e.g., API timeout), catch the exception in middleware and return a structured error message to the LLM (e.g., `Error: Database timeout, please try again`).
- **Self-Correction**: Well-formatted error messages often allow the LLM to understand *why* a call failed and retry it with corrected parameters automatically.
## Samples

View File

@@ -1,42 +0,0 @@
---
title: "Go"
type: docs
weight: 3
description: >
How to add pre- and post- processing to your Agents using Go.
---
## Prerequisites
This tutorial assumes that you have set up MCP Toolbox with a basic agent as described in the [local quickstart](../../getting-started/local_quickstart_go.md).
This guide demonstrates how to implement these patterns in your Toolbox applications.
## Implementation
{{< tabpane persist=header >}}
{{% tab header="ADK" text=true %}}
The following example demonstrates how to use the `beforeToolCallback` and `afterToolCallback` hooks in the ADK `LlmAgent` to implement pre and post processing logic.
```go
{{< include "go/adk/agent.go" >}}
```
You can also add model-level (`beforeModelCallback`, `afterModelCallback`) and agent-level (`beforeAgentCallback`, `afterAgentCallback`) hooks to intercept messages at different stages of the execution loop.
For more information, see the [ADK Callbacks documentation](https://google.github.io/adk-docs/callbacks/types-of-callbacks/).{{% /tab %}}
{{< /tabpane >}}
## Results
The output should look similar to the following.
{{< notice note >}}
The exact responses may vary due to the non-deterministic nature of LLMs and differences between orchestration frameworks.
{{< /notice >}}
```
AI: Booking Confirmed! You earned 500 Loyalty Points with this stay.
AI: Error: Maximum stay duration is 14 days.
```

View File

@@ -1,174 +0,0 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"context"
"fmt"
"log"
"os"
"strings"
"time"
"github.com/googleapis/mcp-toolbox-sdk-go/tbadk"
"google.golang.org/adk/agent"
"google.golang.org/adk/agent/llmagent"
"google.golang.org/adk/model/gemini"
"google.golang.org/adk/runner"
"google.golang.org/adk/session"
"google.golang.org/adk/tool"
"google.golang.org/genai"
)
const systemPrompt = `You're a helpful hotel assistant. You handle hotel searching, booking and
cancellations. When the user searches for a hotel, mention it's name, id,
location and price tier. Always mention hotel ids while performing any
searches. This is very important for any operations. For any bookings or
cancellations, please provide the appropriate confirmation. Be sure to
update checkin or checkout dates if mentioned by the user.
Don't ask for confirmations from the user.`
var queries = []string{
"Book hotel with id 3.",
"Update my hotel with id 3 with checkin date 2025-01-04 and checkout date 2025-01-20",
}
// Pre-processing
func enforceBusinessRules(ctx tool.Context, tool tool.Tool, args map[string]any) (map[string]any, error) {
fmt.Printf("POLICY CHECK: Intercepting '%s'\n", tool.Name())
if tool.Name() == "update-hotel" {
checkinStr, okCheckin := args["checkin_date"].(string)
checkoutStr, okCheckout := args["checkout_date"].(string)
if okCheckin && okCheckout {
startDate, errStart := time.Parse("2006-01-02", checkinStr)
endDate, errEnd := time.Parse("2006-01-02", checkoutStr)
if errStart != nil || errEnd != nil {
return nil, nil
}
duration := endDate.Sub(startDate).Hours() / 24
if duration > 14 {
fmt.Println("BLOCKED: Stay too long")
return map[string]any{"Error": "Maximum stay duration is 14 days."}, nil
}
}
}
return nil, nil
}
// Post-processing
func enrichResponse(ctx tool.Context, tool tool.Tool, args, result map[string]any, err error) (map[string]any, error) {
resultStr := fmt.Sprintf("%v", result)
if tool.Name() == "book-hotel" {
if err != nil {
return nil, err
}
if _, ok := result["Error"]; !ok && !strings.Contains(resultStr, "Error") {
const loyaltyBonus = 500
enrichedResult := fmt.Sprintf("Booking Confirmed!\n You earned %d Loyalty Points with this stay.\n\nSystem Details: %s", loyaltyBonus, resultStr)
return map[string]any{"confirmation": enrichedResult}, nil
}
}
return result, nil
}
func main() {
genaiKey := os.Getenv("GOOGLE_API_KEY")
toolboxURL := "http://localhost:5000"
ctx := context.Background()
toolboxClient, err := tbadk.NewToolboxClient(toolboxURL)
if err != nil {
log.Fatalf("Failed to create MCP Toolbox client: %v", err)
}
toolsetName := "my-toolset"
mcpTools, err := toolboxClient.LoadToolset(toolsetName, ctx)
if err != nil {
log.Fatalf("Failed to load MCP toolset '%s': %v\nMake sure your Toolbox server is running.", toolsetName, err)
}
model, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{
APIKey: genaiKey,
})
if err != nil {
log.Fatalf("Failed to create model: %v", err)
}
tools := make([]tool.Tool, len(mcpTools))
for i := range mcpTools {
tools[i] = &mcpTools[i]
}
llmagent, err := llmagent.New(llmagent.Config{
Name: "hotel_assistant",
Model: model,
Description: "Agent to answer questions about hotels.",
Instruction: systemPrompt,
Tools: tools,
// Add pre- and post- processing hooks
BeforeToolCallbacks: []llmagent.BeforeToolCallback{enforceBusinessRules},
AfterToolCallbacks: []llmagent.AfterToolCallback{enrichResponse},
})
if err != nil {
log.Fatalf("Failed to create agent: %v", err)
}
appName := "hotel_assistant"
userID := "user-123"
sessionService := session.InMemoryService()
respSess, err := sessionService.Create(ctx, &session.CreateRequest{
AppName: appName,
UserID: userID,
})
if err != nil {
log.Fatalf("Failed to create the session service: %v", err)
}
sess := respSess.Session
r, err := runner.New(runner.Config{
AppName: appName,
Agent: llmagent,
SessionService: sessionService,
})
if err != nil {
log.Fatalf("Failed to create runner: %v", err)
}
for i, query := range queries {
fmt.Printf("\n=== Query %d: %s ===\n", i+1, query)
userMsg := genai.NewContentFromText(query, genai.RoleUser)
streamingMode := agent.StreamingModeSSE
runIter := r.Run(ctx, userID, sess.ID(), userMsg, agent.RunConfig{
StreamingMode: streamingMode,
})
fmt.Print("AI: ")
for event := range runIter {
if event != nil && event.Content != nil {
for _, p := range event.Content.Parts {
fmt.Print(p.Text)
}
}
}
fmt.Println("\n" + strings.Repeat("-", 80) + "\n")
}
}

View File

@@ -1,44 +0,0 @@
module example.com/adk-agent
go 1.24.4
require (
github.com/googleapis/mcp-toolbox-sdk-go v0.5.1
google.golang.org/adk v0.3.0
google.golang.org/genai v1.43.0
)
require (
cloud.google.com/go v0.123.0 // indirect
cloud.google.com/go/auth v0.18.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/safehtml v0.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
github.com/googleapis/gax-go/v2 v2.16.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
go.opentelemetry.io/otel v1.39.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/otel/sdk v1.39.0 // indirect
go.opentelemetry.io/otel/trace v1.39.0 // indirect
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/net v0.49.0 // indirect
golang.org/x/oauth2 v0.34.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
google.golang.org/api v0.263.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect
google.golang.org/grpc v1.78.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
rsc.io/omap v1.2.0 // indirect
rsc.io/ordered v1.1.1 // indirect
)

View File

@@ -1,132 +0,0 @@
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE=
cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI=
cloud.google.com/go/secretmanager v1.16.0 h1:19QT7ZsLJ8FSP1k+4esQvuCD7npMJml6hYzilxVyT+k=
cloud.google.com/go/secretmanager v1.16.0/go.mod h1://C/e4I8D26SDTz1f3TQcddhcmiC3rMEl0S1Cakvs3Q=
cloud.google.com/go/storage v1.59.2 h1:gmOAuG1opU8YvycMNpP+DvHfT9BfzzK5Cy+arP+Nocw=
cloud.google.com/go/storage v1.59.2/go.mod h1:cMWbtM+anpC74gn6qjLh+exqYcfmB9Hqe5z6adx+CLI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 h1:lhhYARPUu3LmHysQ/igznQphfzynnqI3D75oUyw1HXk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0/go.mod h1:l9rva3ApbBpEJxSNYnwT9N4CDLrWgtq3u8736C5hyJw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 h1:s0WlVbf9qpvkh1c/uDAPElam0WrL7fHRIidgZJ7UqZI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0=
github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM=
github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo=
github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs=
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8=
github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao=
github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8=
github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y=
github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14=
github.com/googleapis/mcp-toolbox-sdk-go v0.5.1 h1:Jc7IUlVoitpkWK+21ccmzg+213Nv9lyN0tHXv16JPsQ=
github.com/googleapis/mcp-toolbox-sdk-go v0.5.1/go.mod h1:wjOHkYUVD8TwLcAaSbubKj6kY8pfMVCEIxy2OzL4Fu0=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs=
go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/adk v0.3.0 h1:gitgAKnET1F1+fFZc7VSAEo7cjK+D39mnRyqIRTzyzY=
google.golang.org/adk v0.3.0/go.mod h1:iE1Kgc8JtYHiNxfdLa9dxcV4DqTn0D8q4eqhBi012Ak=
google.golang.org/api v0.263.0 h1:UFs7qn8gInIdtk1ZA6eXRXp5JDAnS4x9VRsRVCeKdbk=
google.golang.org/api v0.263.0/go.mod h1:fAU1xtNNisHgOF5JooAs8rRaTkl2rT3uaoNGo9NS3R8=
google.golang.org/genai v1.43.0 h1:8vhqhzJNZu1U94e2m+KvDq/TUUjSmDrs1aKkvTa8SoM=
google.golang.org/genai v1.43.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934=
google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/omap v1.2.0 h1:c1M8jchnHbzmJALzGLclfH3xDWXrPxSUHXzH5C+8Kdw=
rsc.io/omap v1.2.0/go.mod h1:C8pkI0AWexHopQtZX+qiUeJGzvc8HkdgnsWK4/mAa00=
rsc.io/ordered v1.1.1 h1:1kZM6RkTmceJgsFH/8DLQvkCVEYomVDJfBRLT595Uak=
rsc.io/ordered v1.1.1/go.mod h1:evAi8739bWVBRG9aaufsjVc202+6okf8u2QeVL84BCM=

View File

@@ -1,78 +0,0 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"bytes"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
func TestQuickstartSample(t *testing.T) {
framework := os.Getenv("ORCH_NAME")
if framework == "" {
t.Skip("Skipping test: ORCH_NAME environment variable is not set.")
}
t.Logf("--- Testing: %s ---", framework)
if os.Getenv("GOOGLE_API_KEY") == "" {
t.Skipf("Skipping test for %s: GOOGLE_API_KEY environment variable is not set.", framework)
}
sampleDir := filepath.Join(".", framework)
if _, err := os.Stat(sampleDir); os.IsNotExist(err) {
t.Fatalf("Test setup failed: directory for framework '%s' not found.", framework)
}
cmd := exec.Command("go", "run", ".")
cmd.Dir = sampleDir
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
actualOutput := stdout.String()
if err != nil {
t.Fatalf("Script execution failed with error: %v\n--- STDERR ---\n%s", err, stderr.String())
}
if len(actualOutput) == 0 {
t.Fatal("Script ran successfully but produced no output.")
}
goldenKeywords := []string{
"AI:",
"Loyalty Points",
"POLICY CHECK: Intercepting 'update-hotel'",
}
var missingKeywords []string
outputLower := strings.ToLower(actualOutput)
for _, keyword := range goldenKeywords {
kw := strings.TrimSpace(keyword)
if kw != "" && !strings.Contains(outputLower, strings.ToLower(kw)) {
missingKeywords = append(missingKeywords, kw)
}
}
if len(missingKeywords) > 0 {
t.Fatalf("FAIL: The following keywords were missing from the output: [%s]", strings.Join(missingKeywords, ", "))
}
}

View File

@@ -1,47 +0,0 @@
---
title: "Javascript"
type: docs
weight: 2
description: >
How to add pre- and post- processing to your Agents using JS.
---
## Prerequisites
This tutorial assumes that you have set up MCP Toolbox with a basic agent as described in the [local quickstart](../../getting-started/local_quickstart_js.md).
This guide demonstrates how to implement these patterns in your Toolbox applications.
## Implementation
{{< tabpane persist=header >}}
{{% tab header="ADK" text=true %}}
Coming soon.
{{% /tab %}}
{{% tab header="Langchain" text=true %}}
The following example demonstrates how to use `ToolboxClient` with LangChain's middleware to implement pre- and post- processing for tool calls.
```js
{{< include "js/langchain/agent.js" >}}
```
You can also use the `wrapModelCall` hook to intercept messages before and after model calls.
You can also use [node-style hooks](https://docs.langchain.com/oss/javascript/langchain/middleware/custom#node-style-hooks) to intercept messages at the agent and model level.
See the [LangChain Middleware documentation](https://docs.langchain.com/oss/javascript/langchain/middleware/custom#tool-call-monitoring) for details on these additional hook types.
{{% /tab %}}
{{< /tabpane >}}
## Results
The output should look similar to the following.
{{< notice note >}}
The exact responses may vary due to the non-deterministic nature of LLMs and differences between orchestration frameworks.
{{< /notice >}}
```
AI: Booking Confirmed! You earned 500 Loyalty Points with this stay.
AI: Error: Maximum stay duration is 14 days.
```

View File

@@ -1,91 +0,0 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { describe, test, before, after } from "node:test";
import assert from "node:assert/strict";
import path from "path";
import { fileURLToPath } from "url";
const ORCH_NAME = process.env.ORCH_NAME;
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const orchDir = path.join(__dirname, ORCH_NAME);
const agentPath = path.join(orchDir, "agent.js");
const { main: runAgent } = await import(agentPath);
const GOLDEN_KEYWORDS = [
"AI:",
"Loyalty Points",
"POLICY CHECK: Intercepting 'update-hotel'"
];
describe(`${ORCH_NAME} Pre/Post Processing Agent`, () => {
let capturedOutput = [];
let capturedErrors = [];
let originalLog;
let originalError;
before(() => {
originalLog = console.log;
originalError = console.error;
console.log = (...args) => {
const msg = args.map(a => (typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a))).join(' ');
capturedOutput.push(msg);
};
console.error = (...args) => {
const msg = args.map(a => (typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a))).join(' ');
capturedErrors.push(msg);
};
});
after(() => {
console.log = originalLog;
console.error = originalError;
});
test("runs without errors and outputContainsRequiredKeywords", async () => {
capturedOutput = [];
capturedErrors = [];
await runAgent();
assert.equal(
capturedErrors.length,
0,
`Script produced stderr: ${capturedErrors.join("\n")}`
);
const actualOutput = capturedOutput.join("\n");
assert.ok(
actualOutput.length > 0,
"Assertion Failed: Script ran successfully but produced no output."
);
const missingKeywords = [];
for (const keyword of GOLDEN_KEYWORDS) {
if (!actualOutput.includes(keyword)) {
missingKeywords.push(keyword);
}
}
assert.ok(
missingKeywords.length === 0,
`Assertion Failed: The following keywords were missing from the output: [${missingKeywords.join(", ")}]`
);
});
});

View File

@@ -1,109 +0,0 @@
import { ToolboxClient } from "@toolbox-sdk/core";
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
import { createAgent, createMiddleware, ToolMessage } from "langchain";
import { tool } from "@langchain/core/tools";
import { fileURLToPath } from "url";
import process from "process";
const systemPrompt = `
You're a helpful hotel assistant. You handle hotel searching, booking and
cancellations. When the user searches for a hotel, mention it's name, id,
location and price tier. Always mention hotel ids while performing any
searches. This is very important for any operations. For any bookings or
cancellations, please provide the appropriate confirmation. Be sure to
update checkin or checkout dates if mentioned by the user.
Don't ask for confirmations from the user.
`;
const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY || 'your-api-key'; // Replace it with your API key
const businessRulesMiddleware = createMiddleware({
name: "BusinessRules",
wrapToolCall: async (request, handler) => {
const toolName = request.toolCall.name;
const toolArgs = request.toolCall.args;
console.log(`POLICY CHECK: Intercepting '${toolName}' running with args ${JSON.stringify(toolArgs)}`);
if (toolName === "update-hotel" && toolArgs.checkin_date && toolArgs.checkout_date) {
try {
const start = new Date(toolArgs.checkin_date);
const end = new Date(toolArgs.checkout_date);
const duration = (end - start) / (1000 * 60 * 60 * 24); // days
if (duration > 14) {
console.log("BLOCKED: Stay too long");
return ToolMessage({content:'Error: Maximum stay duration is 14 days.', status:"error"})
}
} catch (e) {
// Ignore invalid dates
}
}
return handler(request);
}
});
const enrichmentMiddleware = createMiddleware({
name: "Enrichment",
wrapToolCall: async (request, handler) => {
const result = await handler(request);
const toolName = request.toolCall.name;
let content = result;
if (typeof result === 'object' && result !== null && result.content) {
content = result.content;
}
if (toolName === "book-hotel" && typeof content === 'string' && !content.includes("Error")) {
const loyaltyBonus = 500;
const enrichedContent = `Booking Confirmed!\n You earned ${loyaltyBonus} Loyalty Points with this stay.\n\nSystem Details: ${content}`;
if (typeof result === 'object' && result !== null) {
result.content = enrichedContent;
return result;
}
return enrichedContent;
}
return result;
}
});
const queries = [
"Book hotel with id 3.",
"Update my hotel with id 3 with checkin date 2025-01-18 and checkout date 2025-02-10"
];
async function main() {
const client = new ToolboxClient("http://127.0.0.1:5000");
const rawTools = await client.loadToolset("my-toolset");
const tools = rawTools
.map(t => tool(t, {
name: t.getName(),
description: t.getDescription(),
schema: t.getParamSchema()
}));
const model = new ChatGoogleGenerativeAI({
model: "gemini-2.5-flash",
});
const agent = createAgent({
model: model,
tools: tools,
systemPrompt: systemPrompt,
middleware: [businessRulesMiddleware, enrichmentMiddleware]
});
for (const query of queries) {
console.log(`\nUSER: '${query}'`);
const result = await agent.invoke({
messages: [
{ role: "user", content: query},
],
});
console.log("-".repeat(50));
console.log(`AI: ${result.messages[result.messages.length-1].content}`);
}
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
main();
}
export { main };

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +0,0 @@
{
"name": "langchain",
"version": "1.0.0",
"description": "LangChain.js sample for pre/post processing",
"type": "module",
"main": "agent.js",
"scripts": {
"start": "node agent.js"
},
"dependencies": {
"@langchain/core": "^1.1.26",
"@langchain/google-genai": "^2.1.19",
"@langchain/google-vertexai": "^2.1.19",
"@toolbox-sdk/core": "^0.2.1",
"langchain": "^1.2.25",
"zod": "^3.23.8"
},
"author": "",
"license": "ISC"
}

View File

@@ -8,7 +8,7 @@ description: >
## Prerequisites
This tutorial assumes that you have set up MCP Toolbox with a basic agent as described in the [local quickstart](../../getting-started/local_quickstart.md).
This tutorial assumes that you have set up Toolbox with a basic agent as described in the [local quickstart](../../getting-started/local_quickstart.md).
This guide demonstrates how to implement these patterns in your Toolbox applications.
@@ -16,14 +16,7 @@ This guide demonstrates how to implement these patterns in your Toolbox applicat
{{< tabpane persist=header >}}
{{% tab header="ADK" text=true %}}
The following example demonstrates how to use `ToolboxToolset` with ADK's pre and post processing hooks to implement pre and post processing for tool calls.
```py
{{< include "python/adk/agent.py" >}}
```
You can also add model-level (`before_model_callback`, `after_model_callback`) and agent-level (`before_agent_callback`, `after_agent_callback`) hooks to intercept messages at different stages of the execution loop.
For more information, see the [ADK Callbacks documentation](https://google.github.io/adk-docs/callbacks/types-of-callbacks/).
Coming soon.
{{% /tab %}}
{{% tab header="Langchain" text=true %}}
The following example demonstrates how to use `ToolboxClient` with LangChain's middleware to implement pre- and post- processing for tool calls.
@@ -38,11 +31,7 @@ You can also add model-level (`wrap_model`) and agent-level (`before_agent`, `af
## Results
The output should look similar to the following.
{{< notice note >}}
The exact responses may vary due to the non-deterministic nature of LLMs and differences between orchestration frameworks.
{{< /notice >}}
The output should look similar to the following. Note that exact responses may vary due to the non-deterministic nature of LLMs and differences between orchestration frameworks.
```
AI: Booking Confirmed! You earned 500 Loyalty Points with this stay.

View File

@@ -1,137 +0,0 @@
import asyncio
from datetime import datetime
from typing import Any, Dict, Optional
from copy import deepcopy
from google.adk import Agent
from google.adk.apps import App
from google.adk.runners import Runner
from google.adk.sessions.in_memory_session_service import InMemorySessionService
from google.adk.tools.tool_context import ToolContext
from google.genai import types
from toolbox_adk import CredentialStrategy, ToolboxToolset, ToolboxTool
SYSTEM_PROMPT = """
You're a helpful hotel assistant. You handle hotel searching, booking and
cancellations. When the user searches for a hotel, mention it's name, id,
location and price tier. Always mention hotel ids while performing any
searches. This is very important for any operations. For any bookings or
cancellations, please provide the appropriate confirmation. Be sure to
update checkin or checkout dates if mentioned by the user.
Don't ask for confirmations from the user.
"""
# Pre processing
async def enfore_business_rules(
tool: ToolboxTool, args: Dict[str, Any], tool_context: ToolContext
) -> Optional[Dict[str, Any]]:
"""
Callback fired before a tool is executed.
Enforces business logic: Max stay duration is 14 days.
"""
tool_name = tool.name
print(f"POLICY CHECK: Intercepting '{tool_name}'")
if tool_name == "update-hotel" and "checkin_date" in args and "checkout_date" in args:
start = datetime.fromisoformat(args["checkin_date"])
end = datetime.fromisoformat(args["checkout_date"])
duration = (end - start).days
if duration > 14:
print("BLOCKED: Stay too long")
return {"result": "Error: Maximum stay duration is 14 days."}
return None
# Post processing
async def enrich_response(
tool: ToolboxTool,
args: Dict[str, Any],
tool_context: ToolContext,
tool_response: Any,
) -> Optional[Any]:
"""
Callback fired after a tool execution.
Enriches response for successful bookings.
"""
if isinstance(tool_response, dict):
result = tool_response.get("result", "")
elif isinstance(tool_response, str):
result = tool_response
else:
return None
tool_name = tool.name
if isinstance(result, str) and "Error" not in result:
if tool_name == "book-hotel":
loyalty_bonus = 500
enriched_result = f"Booking Confirmed!\n You earned {loyalty_bonus} Loyalty Points with this stay.\n\nSystem Details: {result}"
if isinstance(tool_response, dict):
modified_response = deepcopy(tool_response)
modified_response["result"] = enriched_result
return modified_response
else:
return enriched_result
return None
async def run_chat_turn(
runner: Runner, session_id: str, user_id: str, message_text: str
):
"""Executes a single chat turn and prints the interaction."""
print(f"\nUSER: '{message_text}'")
response_text = ""
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=types.Content(role="user", parts=[types.Part(text=message_text)]),
):
if event.content and event.content.parts:
for part in event.content.parts:
if part.text:
response_text += part.text
print(f"AI: {response_text}")
async def main():
toolset = ToolboxToolset(
server_url="http://127.0.0.1:5000",
toolset_name="my-toolset",
credentials=CredentialStrategy.toolbox_identity(),
)
tools = await toolset.get_tools()
root_agent = Agent(
name="root_agent",
model="gemini-2.5-flash",
instruction=SYSTEM_PROMPT,
tools=tools,
# add any pre and post processing callbacks
before_tool_callback=enfore_business_rules,
after_tool_callback=enrich_response,
)
app = App(root_agent=root_agent, name="my_agent")
runner = Runner(app=app, session_service=InMemorySessionService())
session_id = "test-session"
user_id = "test-user"
await runner.session_service.create_session(
app_name=app.name, user_id=user_id, session_id=session_id
)
# First turn: Successful booking
await run_chat_turn(runner, session_id, user_id, "Book hotel with id 3.")
print("-" * 50)
# Second turn: Policy violation (stay > 14 days)
await run_chat_turn(
runner,
session_id,
user_id,
"Book a hotel with id 5 with checkin date 2025-01-18 and checkout date 2025-02-10",
)
await toolset.close()
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -1,3 +0,0 @@
google-adk[toolbox]==1.23.0
toolbox-adk==0.5.8
google-genai==1.62.0