Compare commits

...

60 Commits

Author SHA1 Message Date
Twisha Bansal
fe198308d3 Merge branch 'main' into adk-python-processing 2026-02-18 17:16:19 +05:30
Twisha Bansal
77f7990a03 docs: add pre/post processing docs for langchain JS (#2421)
## Description

Trigger has been tested corresponding to local changes. Latest
successful run:
https://pantheon.corp.google.com/cloud-build/builds;region=global/0e7720f6-451c-4a89-8a43-a35131ec90a5;step=0?project=toolbox-testing-438616

Note: After merging, update JS pre and post processing sample testing
trigger.

## PR Checklist

> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:

- [ ] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [ ] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose)
  before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Anubhav Dhawan <anubhavdhawan@google.com>
2026-02-18 17:15:15 +05:30
Twisha Bansal
90ba07dfe0 chore: fix license (#2455)
## Description

> Should include a concise description of the changes (bug or feature),
it's
> impact, along with a summary of the solution

## PR Checklist

> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:

- [ ] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [ ] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose)
  before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>
2026-02-18 10:21:57 +00:00
Twisha Bansal
a206f1afb6 Merge branch 'main' into adk-python-processing 2026-02-18 15:31:43 +05:30
Twisha Bansal
e84a51b660 docs: make branding consistent across quickstart docs (#2498)
## Description

> Should include a concise description of the changes (bug or feature),
it's
> impact, along with a summary of the solution

## PR Checklist

> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:

- [ ] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [ ] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose)
  before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>
2026-02-18 09:58:52 +00:00
Twisha Bansal
1a62677717 Merge branch 'main' into adk-python-processing 2026-02-18 15:22:00 +05:30
manuka rahul
3e7d9b243d docs: link check only on .md files (#2501)
Previously, the link checker ran on all changed files; it has now been
restricted to changed .md files only.

---------

Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2026-02-18 15:06:00 +05:30
Mend Renovate
5a1559e1c8 chore(deps): pin dependencies (#2499)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[peter-evans/create-or-update-comment](https://redirect.github.com/peter-evans/create-or-update-comment)
| action | pinDigest | → `71345be` |
|
[peter-evans/find-comment](https://redirect.github.com/peter-evans/find-comment)
| action | pinDigest | → `b30e6a3` |

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/googleapis/genai-toolbox).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMi4wIiwidXBkYXRlZEluVmVyIjoiNDMuMjIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2026-02-18 08:56:22 +00:00
Twisha Bansal
5b107c53f3 docs: make processing docs consistent across languages (#2497)
## Description

> Should include a concise description of the changes (bug or feature),
it's
> impact, along with a summary of the solution

## PR Checklist

> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:

- [ ] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [ ] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose)
  before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>
2026-02-18 08:30:17 +00:00
manuka rahul
4d51c2a61e docs: optimize link checker (#2482)
Previous Functionality
The original workflow was designed to execute the link checker across
every file in the repository during each run.

Planned Enhancements
To improve efficiency, the process will be bifurcated into three
distinct components:

- Presubmit Check: The link checker will only target files that have
been modified.
- Improved Reporting: Broken links are now reported via a single,
automated comment in the PR conversation (which updates automatically on
subsequent pushes).

<img width="1478" height="1300" alt="Screenshot 2026-02-17 3 47 52 PM"
src="https://github.com/user-attachments/assets/9f52c4d3-43a5-4b3a-96f5-09ba0e49b402"
/>



- Cleaner Logs: Suppressed non-critical redirect warnings and PR comment
now focuses strictly on broken URLs to make debugging faster.

---------

Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2026-02-18 08:06:02 +00:00
Twisha Bansal
c37958af68 Merge branch 'main' into adk-python-processing 2026-02-18 13:13:12 +05:30
Twisha Bansal
3ceec96f8a docs: add pre/post processing docs for ADK Go (#2441)
## Description

> Should include a concise description of the changes (bug or feature),
it's
> impact, along with a summary of the solution

## PR Checklist

> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:

- [ ] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [ ] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose)
  before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
Co-authored-by: Averi Kitsch <akitsch@google.com>
2026-02-18 13:11:19 +05:30
Twisha Bansal
29bf66a8a3 Merge branch 'main' into adk-python-processing 2026-02-18 12:57:20 +05:30
Twisha Bansal
b38d36aeef update method names 2026-02-18 12:56:59 +05:30
Twisha Bansal
06e01aecd1 docs: add pre/post processing best practices (#2435)
## Description

> Should include a concise description of the changes (bug or feature),
it's
> impact, along with a summary of the solution

## PR Checklist

> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:

- [ ] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [ ] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose)
  before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
Co-authored-by: Averi Kitsch <akitsch@google.com>
2026-02-18 07:16:43 +00:00
Twisha Bansal
acf56382d7 add comment 2026-02-11 13:31:15 +05:30
Twisha Bansal
fe2dd13968 fix docs 2026-02-11 13:29:33 +05:30
Twisha Bansal
bf3fb86fd2 remove extra file 2026-02-11 13:28:47 +05:30
Twisha Bansal
d3d62a76d4 remove golden.txt file 2026-02-11 13:28:10 +05:30
Twisha Bansal
8fa8840d77 Merge branch 'main' into adk-python-processing 2026-02-11 13:26:28 +05:30
Twisha Bansal
1ecc3cd604 add to docs 2026-02-09 12:27:03 +05:30
Twisha Bansal
acbb9c15ed Update agent.py 2026-02-09 11:23:58 +05:30
Twisha Bansal
7e175c62a0 Update docs/en/samples/pre_post_processing/python/adk/agent.py
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-09 11:23:23 +05:30
Twisha Bansal
b0947b2adc Update docs/en/samples/pre_post_processing/python/adk/agent.py
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-09 11:22:51 +05:30
Twisha Bansal
3665b78a4b Update docs/en/samples/pre_post_processing/python/adk/agent.py
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-09 11:22:37 +05:30
Twisha Bansal
d7d5e75324 Update docs/en/samples/pre_post_processing/python/adk/agent.py
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-09 11:22:00 +05:30
Twisha Bansal
ad05a12a67 add requirements file 2026-02-09 11:19:34 +05:30
Twisha Bansal
0b23fc950f adk working file 2026-02-09 11:13:09 +05:30
Twisha Bansal
632dd10180 remove license header from docs 2026-02-05 12:14:23 +05:30
Twisha Bansal
9771aa47df Update python.md 2026-02-04 14:58:53 +05:30
Twisha Bansal
a212aedd19 more reliable tests 2026-02-03 18:30:33 +05:30
Twisha Bansal
9210e5555c unnecessary file change removal 2026-02-03 18:15:05 +05:30
Twisha Bansal
b43af71793 fix merge issues 2026-02-03 18:13:49 +05:30
Twisha Bansal
da1f463dd1 more reliable agent queries 2026-02-03 18:08:59 +05:30
Twisha Bansal
3265f7e3a6 better tests 2026-02-03 18:08:59 +05:30
Twisha Bansal
336743f747 add more test case + remove flaky test 2026-02-03 18:08:59 +05:30
Twisha Bansal
911069ae8d Fix tests 2026-02-03 18:08:58 +05:30
Twisha Bansal
cee59d52c3 update requirements file 2026-02-03 18:08:58 +05:30
Twisha Bansal
9517daba09 license fix 2026-02-03 18:08:57 +05:30
Twisha Bansal
3c61ee0597 add sample tests 2026-02-03 18:08:09 +05:30
Twisha Bansal
19271eb9ee docs: clarify that pre/post processing is an orchestration feature
Explicitly document that these capabilities are typically provided by orchestration frameworks (like LangChain, LangGraph) rather than the Toolbox SDK itself, but that Toolbox tools are designed to leverage them.
2026-02-03 18:08:09 +05:30
Twisha Bansal
3a150c77ca Highlight tool level processing 2026-02-03 18:08:09 +05:30
Twisha Bansal
ca6f31a192 fix import 2026-02-03 18:08:09 +05:30
Twisha Bansal
d7faf7700f logic fix 2026-02-03 18:08:09 +05:30
Twisha Bansal
37a60ea2a6 Update docs/en/samples/pre_post_processing/python/langchain/agent.py
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-03 18:08:09 +05:30
Twisha Bansal
8de16976ae license header 2026-02-03 18:08:08 +05:30
Twisha Bansal
49cb2f39f7 lint 2026-02-03 18:08:08 +05:30
Twisha Bansal
f169874e53 gemini code review 2026-02-03 18:08:08 +05:30
Twisha Bansal
db8c3a3c77 remove not needed files 2026-02-03 18:08:08 +05:30
Twisha Bansal
8b33b0c67f docs: add pre/post processing docs for langchain python 2026-02-03 18:08:08 +05:30
Twisha Bansal
35fa73516b rename file 2026-02-03 15:45:03 +05:30
Twisha Bansal
66df3bfd21 move file around fix 2026-02-03 15:40:49 +05:30
Twisha Bansal
73e0edc3cd move files around 2026-02-03 15:36:56 +05:30
Twisha Bansal
3f32a9aab6 fix paths 2026-02-03 15:29:18 +05:30
Twisha Bansal
28006fc9b2 move files around 2026-02-03 15:27:43 +05:30
Twisha Bansal
56c69131b4 Update run_tests.sh 2026-02-03 15:09:07 +05:30
Twisha Bansal
ad4a509340 Apply suggestion from @gemini-code-assist[bot]
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-03 14:16:47 +05:30
Twisha Bansal
d39acac96c Apply suggestion from @gemini-code-assist[bot]
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-03 14:16:36 +05:30
Twisha Bansal
6df2ad28a9 Apply suggestion from @gemini-code-assist[bot]
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-03 14:15:54 +05:30
Twisha Bansal
8416378613 refactor: consolidate quickstart tests into universal runner 2026-02-03 14:11:54 +05:30
23 changed files with 2815 additions and 107 deletions

View File

@@ -0,0 +1,57 @@
# 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

@@ -0,0 +1,57 @@
# 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

132
.github/workflows/link_checker.yaml vendored Normal file
View File

@@ -0,0 +1,132 @@
# 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

@@ -1,68 +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:
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",
"[Toolbox](https://github.com/googleapis/genai-toolbox) end-to-end in Google\n",
"[MCP 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 a Toolbox server.\n",
"- Connect to Toolbox and develop a sample `Hotel Booking` application.\n",
"- Launch an MCP Toolbox server.\n",
"- Connect to MCP Toolbox and develop a sample `Hotel Booking` application.\n",
"\n",
"Here is the simplified flow of a Toolbox Application:\n",
"Here is the simplified flow of a MCP Toolbox Application:\n",
"\n",
"<img src=\"https://services.google.com/fh/files/misc/toolbox_flow.png\" alt=\"Toolbox Flow\"/>\n",
"<img src=\"https://services.google.com/fh/files/misc/toolbox_flow.png\" alt=\"MCP Toolbox Flow\"/>\n",
"\n"
]
},
@@ -208,12 +208,12 @@
"id": "EPuheP8DIt3p"
},
"source": [
"## Step 2: Install and configure Toolbox\n",
"## Step 2: Install and configure MCP Toolbox\n",
"\n",
"In this section, we will\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",
"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",
"\n"
]
},
@@ -223,7 +223,7 @@
"id": "Bl1IeaqZbMYh"
},
"source": [
"Download the [latest](https://github.com/googleapis/genai-toolbox/releases) version of Toolbox as a binary."
"Download the [latest](https://github.com/googleapis/genai-toolbox/releases) version of MCP 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 [Toolbox documentation](https://googleapis.github.io/genai-toolbox/getting-started/configure/).\n",
"For detailed configuration options, please refer to the [MCP 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 toolbox.\n",
"# You can also upload a tools file and use that to run MCP toolbox.\n",
"tools_file_name = \"tools.yml\"\n",
"file_content = f\"\"\"\n",
"kind: sources\n",
@@ -417,7 +417,7 @@
},
"outputs": [],
"source": [
"# Start a toolbox server\n",
"# Start an MCP 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 toolbox is running\n",
"# Check if MCP toolbox is running\n",
"!sudo lsof -i :{SERVER_PORT}"
]
},
@@ -439,10 +439,10 @@
"id": "4yFH4JK7JEAv"
},
"source": [
"## Step 3: Connect your agent to Toolbox\n",
"## Step 3: Connect your agent to MCP Toolbox\n",
"\n",
"In this section, you will\n",
"1. Establish a connection to the tools by creating a Toolbox client.\n",
"1. Establish a connection to the tools by creating an MCP 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 a Toolbox based\n",
"> You can either use LangGraph or LlamaIndex to develop an MCP 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 Toolbox Langchain package\n",
"# Install the MCP 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 Toolbox server\n",
" # Load the tools from the MCP 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 Toolbox LlamaIndex package\n",
"# Install the MCP 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 Toolbox server\n",
" # Load the tools from the MCP 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 Toolbox Core package\n",
"# Install the MCP 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 Toolbox ports.\n",
"Executing this will terminate the processes running on the database and MCP 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 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 MCP 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 Toolbox
## Step 2: Install and configure MCP Toolbox
{{< regionInclude "quickstart/shared/configure_toolbox.md" "configure_toolbox" >}}
## Step 3: Connect your agent to Toolbox
## Step 3: Connect your agent to MCP Toolbox
In this section, we will write and run an agent that will load the Tools
from Toolbox.
from MCP 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 Toolbox:
1. Update `my_agent/agent.py` with the following content to connect to MCP 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 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 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).
---
## 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 Toolbox
## Step 2: Install and configure MCP Toolbox
{{< regionInclude "quickstart/shared/configure_toolbox.md" "configure_toolbox" >}}
## Step 3: Connect your agent to Toolbox
## Step 3: Connect your agent to MCP Toolbox
In this section, we will write and run an agent that will load the Tools
from Toolbox.
from MCP 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 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 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).
---
## 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 Toolbox
## Step 2: Install and configure MCP Toolbox
{{< regionInclude "quickstart/shared/configure_toolbox.md" "configure_toolbox" >}}
## Step 3: Connect your agent to Toolbox
## Step 3: Connect your agent to MCP Toolbox
In this section, we will write and run an agent that will load the Tools
from Toolbox.
from MCP Toolbox.
1. (Optional) Initialize a Node.js project:

View File

@@ -1,3 +1,5 @@
// 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
@@ -9,6 +11,7 @@
// 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

@@ -50,5 +50,30 @@ 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

@@ -0,0 +1,42 @@
---
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

@@ -0,0 +1,174 @@
/*
* 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

@@ -0,0 +1,44 @@
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

@@ -0,0 +1,132 @@
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

@@ -0,0 +1,78 @@
// 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

@@ -0,0 +1,47 @@
---
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

@@ -0,0 +1,91 @@
// 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

@@ -0,0 +1,109 @@
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

@@ -0,0 +1,20 @@
{
"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 Toolbox with a basic agent as described in the [local quickstart](../../getting-started/local_quickstart.md).
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 guide demonstrates how to implement these patterns in your Toolbox applications.
@@ -16,7 +16,14 @@ This guide demonstrates how to implement these patterns in your Toolbox applicat
{{< tabpane persist=header >}}
{{% tab header="ADK" text=true %}}
Coming soon.
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/).
{{% /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.
@@ -31,7 +38,11 @@ You can also add model-level (`wrap_model`) and agent-level (`before_agent`, `af
## Results
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.
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.

View File

@@ -0,0 +1,137 @@
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

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