Compare commits

...

74 Commits

Author SHA1 Message Date
trehanshakuntG
2ef01b0413 Merge branch 'trehanshakunt-firestore-update-doc' of https://github.com/googleapis/genai-toolbox into trehanshakunt-firestore-update-doc 2025-08-20 10:54:47 +05:30
trehanshakuntG
d4ae67f995 feat(tools/firestore-update-document): Add firestore-update-document tool 2025-08-20 10:54:43 +05:30
Twisha Bansal
f632e9e46e docs: clarify versioning pre 1.0.0 (#1184)
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-08-20 10:54:43 +05:30
Dr. Strangelove
81a46552ca docs(looker): minor changes to docs (#1188)
* fixed links to images in `looker_mcp_inspector` by moving it to own
directory.
* added Gemini-CLI section to looker_mcp.md.
* added newer tools to list in looker_mcp.md.

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:43 +05:30
Mend Renovate
b25ecd352b chore(deps): update module google.golang.org/api to v0.248.0 (#1187)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[google.golang.org/api](https://redirect.github.com/googleapis/google-api-go-client)
| `v0.247.0` -> `v0.248.0` |
[![age](https://developer.mend.io/api/mc/badges/age/go/google.golang.org%2fapi/v0.248.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/google.golang.org%2fapi/v0.247.0/v0.248.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>googleapis/google-api-go-client
(google.golang.org/api)</summary>

###
[`v0.248.0`](https://redirect.github.com/googleapis/google-api-go-client/releases/tag/v0.248.0)

[Compare
Source](https://redirect.github.com/googleapis/google-api-go-client/compare/v0.247.0...v0.248.0)

##### Features

- **all:** Auto-regenerate discovery clients
([#&#8203;3272](https://redirect.github.com/googleapis/google-api-go-client/issues/3272))
([8f03c8e](8f03c8e1e1))
- **all:** Auto-regenerate discovery clients
([#&#8203;3274](https://redirect.github.com/googleapis/google-api-go-client/issues/3274))
([2900298](29002988e8))
- **all:** Auto-regenerate discovery clients
([#&#8203;3275](https://redirect.github.com/googleapis/google-api-go-client/issues/3275))
([4c66642](4c6664201e))
- **all:** Auto-regenerate discovery clients
([#&#8203;3276](https://redirect.github.com/googleapis/google-api-go-client/issues/3276))
([2692170](2692170524))
- **all:** Auto-regenerate discovery clients
([#&#8203;3277](https://redirect.github.com/googleapis/google-api-go-client/issues/3277))
([23daa11](23daa11938))
- **all:** Auto-regenerate discovery clients
([#&#8203;3279](https://redirect.github.com/googleapis/google-api-go-client/issues/3279))
([71f2c5f](71f2c5fe1e))
- **all:** Auto-regenerate discovery clients
([#&#8203;3280](https://redirect.github.com/googleapis/google-api-go-client/issues/3280))
([59f18fd](59f18fd7f2))

</details>

---

### 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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS44MS4yIiwidXBkYXRlZEluVmVyIjoiNDEuODEuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2025-08-20 10:54:43 +05:30
Twisha Bansal
bb003699f3 docs: add AlloyDB AI NL codelab (#1149)
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-08-20 10:54:43 +05:30
Haoming Chen
695338f08b feat(tools/bigquery-forecast): Add bigqueryforecast tool (#1148)
This tool wraps the BigQuery's AI.FORECAST function to do the time
series forecasting.

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:43 +05:30
Averi Kitsch
ec846feedc docs: remove database from MongoDB source (#1166)
## 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/langchain-google-alloydb-pg-python/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>
2025-08-20 10:54:43 +05:30
zhouyh
cb06b7af17 feat(tools/oceanbase): Add oceanbase Source and Tool (#895)
1.Added oceanbase Source and Tool
2.Add tests related to OceanBase
3.Add the documentation for Oceanbase
Close #894

---------

Co-authored-by: Wenxin Du <117315983+duwenxin99@users.noreply.github.com>
Co-authored-by: duwenxin <duwenxin@google.com>
2025-08-20 10:54:43 +05:30
trehanshakuntG
ef46db8896 Add strconv function for str to int conversion 2025-08-20 10:54:43 +05:30
Mend Renovate
775cbd9c03 chore(deps): update module github.com/neo4j/neo4j-go-driver/v5 to v5.28.2 (#1163)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[github.com/neo4j/neo4j-go-driver/v5](https://redirect.github.com/neo4j/neo4j-go-driver)
| `v5.28.1` -> `v5.28.2` |
[![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fneo4j%2fneo4j-go-driver%2fv5/v5.28.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fneo4j%2fneo4j-go-driver%2fv5/v5.28.1/v5.28.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>neo4j/neo4j-go-driver
(github.com/neo4j/neo4j-go-driver/v5)</summary>

###
[`v5.28.2`](https://redirect.github.com/neo4j/neo4j-go-driver/releases/tag/v5.28.2)

[Compare
Source](https://redirect.github.com/neo4j/neo4j-go-driver/compare/v5.28.1...v5.28.2)

See https://github.com/neo4j/neo4j-go-driver/wiki/5.x-changelog for more
information.

</details>

---

### 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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS43MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuNzEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-08-20 10:54:43 +05:30
Yuan Teoh
373a19a9c6 docs: remove pr template folder (#1162)
PR template is not working. Since there is only one template, the folder
is irrelevant.
2025-08-20 10:54:43 +05:30
Yuan Teoh
6c9f0a061e docs: update CHANGELOG.md (#1160)
Fix wrong scope in release notes. Updated in release notes.
2025-08-20 10:54:43 +05:30
release-please[bot]
0442d51db5 chore(main): release 0.12.0 (#1085)
🤖 I have created a release *beep* *boop*
---


##
[0.12.0](https://github.com/googleapis/genai-toolbox/compare/v0.11.0...v0.12.0)
(2025-08-14)


### Features

* **prebuiltconfig:** Introduce additional parameter to limit context in
list_tables
([#1151](https://github.com/googleapis/genai-toolbox/issues/1151))
([497d3b1](497d3b126d))
* **prebuiltconfig/alloydb-admin:** Add list cluster, instance and users
([#1126](https://github.com/googleapis/genai-toolbox/issues/1126))
([b42c139](b42c139158))
* **prebuiltconfig/alloydb-postgres:** Add tool to create user via Built
in user type or IAM
([#1130](https://github.com/googleapis/genai-toolbox/issues/1130))
([f5bcb9c](f5bcb9c755))
* **source/http:** Add User Agent to `http` invocations
([#1102](https://github.com/googleapis/genai-toolbox/issues/1102))
([6f55b78](6f55b78e96))
* **sources/postgres:** Add support for `queryParams`
([#1047](https://github.com/googleapis/genai-toolbox/issues/1047))
([7b57251](7b57251402)),
closes [#963](https://github.com/googleapis/genai-toolbox/issues/963)
* **tools/bigquery-execute-sql:** Add dry run support
([#1057](https://github.com/googleapis/genai-toolbox/issues/1057))
([1cac9b5](1cac9b5b37))
* **tools/dataplex-search-aspect-types:** Add support for
`dataplex-search-aspect-types` tool
([#1061](https://github.com/googleapis/genai-toolbox/issues/1061))
([d940187](d940187c85))
* **tools/looker:** Add `looker-make-look` tool to create Looks
([#1099](https://github.com/googleapis/genai-toolbox/issues/1099))
([61d9489](61d9489344))
* **tools/looker:** Add visualizations to `query-url` tool
([#1090](https://github.com/googleapis/genai-toolbox/issues/1090))
([5bf2758](5bf275846a))
* **tools/looker:** New Looker tools for dashboards
([#1118](https://github.com/googleapis/genai-toolbox/issues/1118))
([42be3f5](42be3f550c))
* **ui:** Add login with google button for automatic id token retrieval
([#1044](https://github.com/googleapis/genai-toolbox/issues/1044))
([d91bdfc](d91bdfcbdc))


### Bug Fixes

* Correct the capitalization of `map` manifests
([#1139](https://github.com/googleapis/genai-toolbox/issues/1139))
([0b0457c](0b0457c8e6))
* Remove unnecessary fields from `map` parameter manifests
([#1138](https://github.com/googleapis/genai-toolbox/issues/1138))
([fbe8c1a](fbe8c1a9c0))
* **tools/looker:** Add authorized invocation feature to all Looker
tools ([#1091](https://github.com/googleapis/genai-toolbox/issues/1091))
([3b1cce7](3b1cce72e7))
* Update ui info log to reflect port
([#1125](https://github.com/googleapis/genai-toolbox/issues/1125))
([6d691d5](6d691d582f))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-08-20 10:54:42 +05:30
Yuan Teoh
5e7a50fc8d docs: update instructions for PR title and description (#1115)
Add instructions to `DEVELOPER.md` on PR title and PR description. This
also includes the commonly used `type` for PR titles.
2025-08-20 10:54:42 +05:30
Ajaykumar Yadav
802edf6c01 docs: fix typos across docs (#1154)
got some typos while reading the docs

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:42 +05:30
Mend Renovate
5d621af96f chore(deps): update module github.com/cenkalti/backoff/v5 to v5.0.3 (#1156)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[github.com/cenkalti/backoff/v5](https://redirect.github.com/cenkalti/backoff)
| `v5.0.2` -> `v5.0.3` |
[![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fcenkalti%2fbackoff%2fv5/v5.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fcenkalti%2fbackoff%2fv5/v5.0.2/v5.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>cenkalti/backoff (github.com/cenkalti/backoff/v5)</summary>

###
[`v5.0.3`](https://redirect.github.com/cenkalti/backoff/compare/v5.0.2...v5.0.3)

[Compare
Source](https://redirect.github.com/cenkalti/backoff/compare/v5.0.2...v5.0.3)

</details>

---

### 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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS43MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuNzEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2025-08-20 10:54:42 +05:30
Anuj Jhunjhunwala
332110050e feat(tools/dataplex-search-aspect-types): Add support for dataplex-search-aspect-types tool (#1061)
Added support for search aspect types tool in Dataplex.
Fixes #1056

---------

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:42 +05:30
Yuan Teoh
aef1100ece chore: refactor process parameters function (#1145)
Added check to `ProcessParameters()` to check for parameter name
duplication and simplify the function.

ref: #1135
2025-08-20 10:54:42 +05:30
trehanshakuntG
d69a0adc89 Address requested changes 2025-08-20 10:54:42 +05:30
Twisha Bansal
8d7925e207 docs: fix genkit module installation (#1155) 2025-08-20 10:54:42 +05:30
Wenxin Du
8a7eadb07b fix: Correct the capitalization of map manifests (#1139)
Manifests field keys should start with lower case
2025-08-20 10:54:42 +05:30
Wenxin Du
538c8ac317 chore: Use environment variable for Cloud Build project ID (#1152) 2025-08-20 10:54:42 +05:30
prernakakkar-google
8532561b7d feat(prebuiltconfig/alloydb-postgres): add tool to create user via Built in user type or IAM (#1130)
…ilt in or IAM

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:42 +05:30
prernakakkar-google
c6fafc0b7b feat(prebuiltconfig/alloydb-admin): Add list cluster, instance and users (#1126)
…er tool

---------

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:42 +05:30
Wenxin Du
c6e5d52b40 fix: Remove unnecessary fields from map parameter manifests (#1138)
Previously the `additionalProperties` field manifests returns name,
descriptions, authSources etc. that are not needed.
These fields are removed for clarity.
2025-08-20 10:54:42 +05:30
Sri Varshitha
ceb2ac3f5d feat: Introduce additional parameter to limit context in list_tables (#1151)
Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:42 +05:30
Mend Renovate
45872e1fb8 chore(deps): update module cloud.google.com/go/cloudsqlconn to v1.18.0 (#1147)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[cloud.google.com/go/cloudsqlconn](https://redirect.github.com/googlecloudplatform/cloud-sql-go-connector)
| `v1.17.3` -> `v1.18.0` |
[![age](https://developer.mend.io/api/mc/badges/age/go/cloud.google.com%2fgo%2fcloudsqlconn/v1.18.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/cloud.google.com%2fgo%2fcloudsqlconn/v1.17.3/v1.18.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

<details>
<summary>googlecloudplatform/cloud-sql-go-connector
(cloud.google.com/go/cloudsqlconn)</summary>

[`v1.18.0`](https://redirect.github.com/GoogleCloudPlatform/cloud-sql-go-connector/releases/tag/v1.18.0)

[Compare
Source](https://redirect.github.com/googlecloudplatform/cloud-sql-go-connector/compare/v1.17.3...v1.18.0)

- Configure the connector with Google Auth Credentials.
([#&#8203;997](https://redirect.github.com/GoogleCloudPlatform/cloud-sql-go-connector/issues/997))
([594e083](594e083443))

</details>

---

📅 **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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS42MC40IiwidXBkYXRlZEluVmVyIjoiNDEuNjAuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2025-08-20 10:54:42 +05:30
trehanshakuntG
eca3b13841 Update documentation 2025-08-20 10:54:11 +05:30
trehanshakuntG
636c29d106 Update lint changes 2025-08-20 10:54:11 +05:30
trehanshakuntG
e75decdb3a refactor converter_test 2025-08-20 10:54:11 +05:30
trehanshakuntG
53f6cbb4f6 Update firestore test teardown 2025-08-20 10:54:11 +05:30
Yuan Teoh
3607981bd4 feat(sources/postgres): add support for queryParams (#1047)
Add support for `queryParams` for users that would like to connect with
additional query parameters.

```
sources:
    my-pg-source:
        kind: postgres
        host: 127.0.0.1
        port: 5432
        database: my_db
        user: ${USER_NAME}
        password: ${PASSWORD}
        queryParams:
            sslmode: verify-full
            sslrootcert: /tmp/ca.crt
```

`queryParams` will be added as raw query of the database connection url.

Fixes #963
2025-08-20 10:54:11 +05:30
Wenxin Du
fd5547f583 docs: Update Redis and Valkey docs (#1143) 2025-08-20 10:54:11 +05:30
Wenxin Du
4fc9161f9c chore(tools/http): Refactor HTTP manifest creation (#1135)
Simplify manifest creation and replace duplication checking with common
helper.
2025-08-20 10:54:11 +05:30
dishaprakash
a5e92331f8 docs: Add auth in Go SDK sample to the docsite (#1131)
docs: Add auth usage in Go SDK sample to the docsite

This PR adds Go samples on usage of the GetGoogleIDToken auth function
and usage of auth getters.
2025-08-20 10:54:11 +05:30
Mend Renovate
550c12ae44 chore(deps): update module github.com/redis/go-redis/v9 to v9.12.1 (#1132)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[github.com/redis/go-redis/v9](https://redirect.github.com/redis/go-redis)
| `v9.12.0` -> `v9.12.1` |
[![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fredis%2fgo-redis%2fv9/v9.12.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fredis%2fgo-redis%2fv9/v9.12.0/v9.12.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>redis/go-redis (github.com/redis/go-redis/v9)</summary>

###
[`v9.12.1`](https://redirect.github.com/redis/go-redis/releases/tag/v9.12.1):
9.12.1

[Compare
Source](https://redirect.github.com/redis/go-redis/compare/v9.12.0...v9.12.1)

#### 🚀 Highlights

In the last version (9.12.0) the client introduced bigger write and read
buffer sizes. The default value was 512KiB.
However, users reported that this is too big for most use cases and can
lead to high memory usage.
In this version the default value is changed to 256KiB. The `README.md`
was updated to reflect the
correct default value and include a note that the default value can be
changed.

#### 🐛 Bug Fixes

- fix(options): Add buffer sizes to failover. Update README
([#&#8203;3468](https://redirect.github.com/redis/go-redis/pull/3468))

#### 🧰 Maintenance

- fix(options): Add buffer sizes to failover. Update README
([#&#8203;3468](https://redirect.github.com/redis/go-redis/pull/3468))
- chore: update & fix otel example
([#&#8203;3466](https://redirect.github.com/redis/go-redis/pull/3466))

#### Contributors

We'd like to thank all the contributors who worked on this release!

[@&#8203;ndyakov](https://redirect.github.com/ndyakov) and
[@&#8203;vmihailenco](https://redirect.github.com/vmihailenco)

</details>

---

### 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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS42MC40IiwidXBkYXRlZEluVmVyIjoiNDEuNjAuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2025-08-20 10:54:11 +05:30
Mend Renovate
534a71713a chore(deps): update module cloud.google.com/go/alloydbconn to v1.15.5 (#1134)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[cloud.google.com/go/alloydbconn](https://redirect.github.com/googlecloudplatform/alloydb-go-connector)
| `v1.15.4` -> `v1.15.5` |
[![age](https://developer.mend.io/api/mc/badges/age/go/cloud.google.com%2fgo%2falloydbconn/v1.15.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/cloud.google.com%2fgo%2falloydbconn/v1.15.4/v1.15.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>googlecloudplatform/alloydb-go-connector
(cloud.google.com/go/alloydbconn)</summary>

###
[`v1.15.5`](https://redirect.github.com/GoogleCloudPlatform/alloydb-go-connector/releases/tag/v1.15.5)

[Compare
Source](https://redirect.github.com/googlecloudplatform/alloydb-go-connector/compare/v1.15.4...v1.15.5)

##### Bug Fixes

- update dependencies to latest
([#&#8203;700](https://redirect.github.com/GoogleCloudPlatform/alloydb-go-connector/issues/700))
([dbc02c0](dbc02c019f))

</details>

---

### 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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS42MC40IiwidXBkYXRlZEluVmVyIjoiNDEuNjAuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:11 +05:30
Dr. Strangelove
4d1fb18ffb feat(tools/looker): New Looker tools for dashboards (#1118)
* get_dashboards
* make_dashboard
* add_dashboard_element
2025-08-20 10:54:11 +05:30
prernakakkar-google
3b95b2de06 feat(source/http): Add User Agent to calls (#1102)
This change adds a User Agent for all http calls as part of source. This
will append the user agent being passed as part of prebuilt tools
context to default headers.
2025-08-20 10:54:11 +05:30
stone-py
a2b0ed2462 fix: inputting the -ui and -p parameters, the output information is i… (#1125)
When inputting the -ui and -p parameters, the output information is
incorrect

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:11 +05:30
Mend Renovate
da9cf2f8ef chore(deps): update github actions to v5 (major) (#1129)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | major | `v4.2.2` -> `v5.0.0` |
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | major | `v4` -> `v5` |

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

###
[`v5.0.0`](https://redirect.github.com/actions/checkout/releases/tag/v5.0.0)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.3.0...v5.0.0)

##### What's Changed

- Update actions checkout to use node 24 by
[@&#8203;salmanmkc](https://redirect.github.com/salmanmkc) in
[https://github.com/actions/checkout/pull/2226](https://redirect.github.com/actions/checkout/pull/2226)
- Prepare v5.0.0 release by
[@&#8203;salmanmkc](https://redirect.github.com/salmanmkc) in
[https://github.com/actions/checkout/pull/2238](https://redirect.github.com/actions/checkout/pull/2238)

##### ⚠️ Minimum Compatible Runner Version

**v2.327.1**\
[Release
Notes](https://redirect.github.com/actions/runner/releases/tag/v2.327.1)

Make sure your runner is updated to this version or newer to use this
release.

**Full Changelog**:
https://github.com/actions/checkout/compare/v4...v5.0.0

###
[`v4.3.0`](https://redirect.github.com/actions/checkout/releases/tag/v4.3.0)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.2.2...v4.3.0)

##### What's Changed

- docs: update README.md by
[@&#8203;motss](https://redirect.github.com/motss) in
[https://github.com/actions/checkout/pull/1971](https://redirect.github.com/actions/checkout/pull/1971)
- Add internal repos for checking out multiple repositories by
[@&#8203;mouismail](https://redirect.github.com/mouismail) in
[https://github.com/actions/checkout/pull/1977](https://redirect.github.com/actions/checkout/pull/1977)
- Documentation update - add recommended permissions to Readme by
[@&#8203;benwells](https://redirect.github.com/benwells) in
[https://github.com/actions/checkout/pull/2043](https://redirect.github.com/actions/checkout/pull/2043)
- Adjust positioning of user email note and permissions heading by
[@&#8203;joshmgross](https://redirect.github.com/joshmgross) in
[https://github.com/actions/checkout/pull/2044](https://redirect.github.com/actions/checkout/pull/2044)
- Update README.md by
[@&#8203;nebuk89](https://redirect.github.com/nebuk89) in
[https://github.com/actions/checkout/pull/2194](https://redirect.github.com/actions/checkout/pull/2194)
- Update CODEOWNERS for actions by
[@&#8203;TingluoHuang](https://redirect.github.com/TingluoHuang) in
[https://github.com/actions/checkout/pull/2224](https://redirect.github.com/actions/checkout/pull/2224)
- Update package dependencies by
[@&#8203;salmanmkc](https://redirect.github.com/salmanmkc) in
[https://github.com/actions/checkout/pull/2236](https://redirect.github.com/actions/checkout/pull/2236)
- Prepare release v4.3.0 by
[@&#8203;salmanmkc](https://redirect.github.com/salmanmkc) in
[https://github.com/actions/checkout/pull/2237](https://redirect.github.com/actions/checkout/pull/2237)

##### New Contributors

- [@&#8203;motss](https://redirect.github.com/motss) made their first
contribution in
[https://github.com/actions/checkout/pull/1971](https://redirect.github.com/actions/checkout/pull/1971)
- [@&#8203;mouismail](https://redirect.github.com/mouismail) made their
first contribution in
[https://github.com/actions/checkout/pull/1977](https://redirect.github.com/actions/checkout/pull/1977)
- [@&#8203;benwells](https://redirect.github.com/benwells) made their
first contribution in
[https://github.com/actions/checkout/pull/2043](https://redirect.github.com/actions/checkout/pull/2043)
- [@&#8203;nebuk89](https://redirect.github.com/nebuk89) made their
first contribution in
[https://github.com/actions/checkout/pull/2194](https://redirect.github.com/actions/checkout/pull/2194)
- [@&#8203;salmanmkc](https://redirect.github.com/salmanmkc) made their
first contribution in
[https://github.com/actions/checkout/pull/2236](https://redirect.github.com/actions/checkout/pull/2236)

**Full Changelog**:
https://github.com/actions/checkout/compare/v4...v4.3.0

</details>

---

### 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.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS42MC40IiwidXBkYXRlZEluVmVyIjoiNDEuNjAuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:11 +05:30
Twisha Bansal
6ecd8c7982 docs: add js samples to docsite (#1127) 2025-08-20 10:54:11 +05:30
Averi Kitsch
215423429e chore: add "waiting for response" label (#1113)
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-08-20 10:54:11 +05:30
Averi Kitsch
04a4a8e88f docs: fix links (#1114) 2025-08-20 10:54:11 +05:30
AlexTalreja
ae8fe0cd36 chore(ui): logo redirects to homepage for Toolbox UI (#1112)
Clicking on the MCP Toolbox Logo will redirect to the Toolbox UI
Homepage.
2025-08-20 10:54:11 +05:30
AlexTalreja
3aee57c1fe docs(ui): add Toolbox UI login with google feature to docs (#1111)
Add `Login with Google` feature to Toolbox docs
2025-08-20 10:54:10 +05:30
Ganga4060
5044bf8836 docs: Fix broken links (#1106)
Fixed broken links for template parameters in couchbase and MySQL in
tools.

Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-08-20 10:54:10 +05:30
Mend Renovate
ceb118ae2a chore(deps): update dependency go to v1.24.6 (#1094)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [go](https://go.dev/)
([source](https://redirect.github.com/golang/go)) | toolchain | patch |
`1.24.5` -> `1.24.6` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>golang/go (go)</summary>

###
[`v1.24.6`](https://redirect.github.com/golang/go/compare/go1.24.5...go1.24.6)

</details>

---

### 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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS41MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuNTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2025-08-20 10:54:10 +05:30
Dr. Strangelove
f598c6ad92 docs(looker): Minor changes to docs to be uniform with everything else (#1110)
* Renamed the tools docs with dashes instead of underscores.
* Added make_look tool to list in how-to/connect-ide/looker_mcp.md

Co-authored-by: Wenxin Du <117315983+duwenxin99@users.noreply.github.com>
2025-08-20 10:54:10 +05:30
AlexTalreja
962011aa64 feat: add instructions and links to docs in UI (#1100)
Add instructions on basic usage + links to the public documentation on
the main content area of the Homepage, Tools page, and Toolsets page.
2025-08-20 10:54:10 +05:30
Mend Renovate
75c590a2e1 chore(deps): update module google.golang.org/api to v0.246.0 (#1092)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[google.golang.org/api](https://redirect.github.com/googleapis/google-api-go-client)
| `v0.245.0` -> `v0.246.0` |
[![age](https://developer.mend.io/api/mc/badges/age/go/google.golang.org%2fapi/v0.246.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/google.golang.org%2fapi/v0.245.0/v0.246.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

<details>
<summary>googleapis/google-api-go-client
(google.golang.org/api)</summary>

[`v0.246.0`](https://redirect.github.com/googleapis/google-api-go-client/releases/tag/v0.246.0)

[Compare
Source](https://redirect.github.com/googleapis/google-api-go-client/compare/v0.245.0...v0.246.0)

- **all:** Auto-regenerate discovery clients
([#&#8203;3261](https://redirect.github.com/googleapis/google-api-go-client/issues/3261))
([b792200](b792200673))

- **idtoken:** Don't assume DefaultTransport is a http.Transport
([#&#8203;3263](https://redirect.github.com/googleapis/google-api-go-client/issues/3263))
([61fba51](61fba51991)),
refs
[#&#8203;3260](https://redirect.github.com/googleapis/google-api-go-client/issues/3260)

</details>

---

📅 **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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS41MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuNTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-08-20 10:54:10 +05:30
Mend Renovate
516f0ea0b1 chore(deps): update actions/cache digest to 0400d5f (#1108)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/cache](https://redirect.github.com/actions/cache) | action |
digest | `5a3ec84` -> `0400d5f` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### 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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS41MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuNTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2025-08-20 10:53:27 +05:30
Mend Renovate
56cd2d79b6 chore(deps): update module github.com/valkey-io/valkey-go to v1.0.64 (#1087)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[github.com/valkey-io/valkey-go](https://redirect.github.com/valkey-io/valkey-go)
| `v1.0.63` -> `v1.0.64` |
[![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fvalkey-io%2fvalkey-go/v1.0.64?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fvalkey-io%2fvalkey-go/v1.0.63/v1.0.64?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>valkey-io/valkey-go (github.com/valkey-io/valkey-go)</summary>

###
[`v1.0.64`](https://redirect.github.com/valkey-io/valkey-go/releases/tag/v1.0.64):
1.0.64

[Compare
Source](https://redirect.github.com/valkey-io/valkey-go/compare/v1.0.63...v1.0.64)

##### Changes

- feat: Add `FieldValueIter` to `Hsetex` command builder
- feat: Add `CreateAndAliasIndex` to `om.Repository`
- fix: clear placeholder pipeline errors in `valkeycompat`
([#&#8203;61](https://redirect.github.com/valkey-io/valkey-go/issues/61))

##### Contributors

We'd like to thank all the contributors who worked on this release!

[@&#8203;SoulPancake](https://redirect.github.com/SoulPancake),
[@&#8203;XHanL](https://redirect.github.com/XHanL),
[@&#8203;rueian](https://redirect.github.com/rueian) and
[@&#8203;zeeshan-zomato](https://redirect.github.com/zeeshan-zomato)

</details>

---

### 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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS41MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuNTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-08-20 10:53:27 +05:30
Dr. Strangelove
ee9d9038ff feat(tools/looker): add looker-make-look tool to create Looks (#1099) 2025-08-20 10:53:27 +05:30
trehanshakuntG
f0dde117ee feat(tools/firestore-update-document): Add firestore-update-document tool 2025-08-20 10:50:58 +05:30
trehanshakuntG
1e38297977 Merge branch 'main' of https://github.com/googleapis/genai-toolbox into trehanshakunt-firestore-update-doc 2025-08-20 10:49:41 +05:30
trehanshakuntG
8b5ba22017 Merge branch 'main' of https://github.com/googleapis/genai-toolbox into trehanshakunt-firestore-update-doc 2025-08-19 11:39:38 +05:30
trehanshakuntG
5bcf376ca1 Add strconv function for str to int conversion 2025-08-18 18:31:37 +05:30
trehanshakuntG
a3110d1678 Merge branch 'main' of https://github.com/googleapis/genai-toolbox into trehanshakunt-firestore-add-doc 2025-08-18 15:44:45 +05:30
trehanshakuntG
9d9adaac93 Address requested changes 2025-08-18 15:05:59 +05:30
trehanshakuntG
3522a723b8 Merge branch 'main' into trehanshakunt-firestore-add-doc 2025-08-14 23:38:39 +05:30
trehanshakuntG
42e09bff01 Merge branch 'main' of https://github.com/googleapis/genai-toolbox into trehanshakunt-firestore-add-doc 2025-08-14 18:00:17 +05:30
trehanshakuntG
ca324966bc Update documentation 2025-08-13 21:46:37 +05:30
trehanshakuntG
93d0a9b4c0 Update lint changes 2025-08-13 21:43:26 +05:30
trehanshakuntG
32d478cd20 refactor converter_test 2025-08-13 15:22:35 +05:30
trehanshakuntG
0ec910ca9b Update firestore test teardown 2025-08-13 14:43:54 +05:30
trehanshakuntG
d06377d561 Merge branch 'main' of https://github.com/googleapis/genai-toolbox into trehanshakunt-firestore-add-doc 2025-08-13 13:44:38 +05:30
trehanshakuntG
02884910dc Update integration tests 2025-08-13 13:42:54 +05:30
trehanshakuntG
e5a4851605 converter tests fixed 2025-08-07 19:23:50 +05:30
trehanshakuntG
ffedbe1739 Update converter code for referenceValue 2025-08-07 18:58:13 +05:30
trehanshakuntG
4593f9eb31 Update root_test.go file 2025-08-07 18:35:52 +05:30
trehanshakuntG
48012638af add prebuilt tools to root.go 2025-08-07 18:34:43 +05:30
trehanshakuntG
185c29e07e feat: Add firestore-add-documents tool 2025-08-07 18:22:05 +05:30
8 changed files with 1399 additions and 8 deletions

View File

@@ -62,6 +62,7 @@ import (
_ "github.com/googleapis/genai-toolbox/internal/tools/firestore/firestoregetrules"
_ "github.com/googleapis/genai-toolbox/internal/tools/firestore/firestorelistcollections"
_ "github.com/googleapis/genai-toolbox/internal/tools/firestore/firestorequerycollection"
_ "github.com/googleapis/genai-toolbox/internal/tools/firestore/firestoreupdatedocument"
_ "github.com/googleapis/genai-toolbox/internal/tools/firestore/firestorevalidaterules"
_ "github.com/googleapis/genai-toolbox/internal/tools/http"
_ "github.com/googleapis/genai-toolbox/internal/tools/looker/lookeradddashboardelement"

View File

@@ -1260,7 +1260,7 @@ func TestPrebuiltTools(t *testing.T) {
wantToolset: server.ToolsetConfigs{
"firestore-database-tools": tools.ToolsetConfig{
Name: "firestore-database-tools",
ToolNames: []string{"firestore-get-documents", "firestore-add-documents", "firestore-list-collections", "firestore-delete-documents", "firestore-query-collection", "firestore-get-rules", "firestore-validate-rules"},
ToolNames: []string{"firestore-get-documents", "firestore-add-documents", "firestore-update-document", "firestore-list-collections", "firestore-delete-documents", "firestore-query-collection", "firestore-get-rules", "firestore-validate-rules"},
},
},
},

View File

@@ -0,0 +1,326 @@
---
title: "firestore-update-document"
type: docs
weight: 2
description: >
A "firestore-update-document" tool updates an existing document in Firestore.
aliases:
- /resources/tools/firestore-update-document
---
## Description
The `firestore-update-document` tool allows you to update existing documents in Firestore. It supports all Firestore data types using Firestore's native JSON format. The tool can perform both full document updates (replacing all fields) or selective field updates using an update mask. It also supports field deletion when using selective updates.
## Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `documentPath` | string | Yes | The path of the document which needs to be updated |
| `documentData` | map | Yes | The data to update in the document. Must use [Firestore's native JSON format](https://cloud.google.com/firestore/docs/reference/rest/Shared.Types/ArrayValue#Value) with typed values |
| `updateMask` | array | No | The selective fields to update. If not provided, all fields in documentData will be updated. When provided, only the specified fields will be updated |
| `returnData` | boolean | No | If set to true, the output will include the data of the updated document. Defaults to false to help avoid overloading the context |
## Output
The tool returns a map containing:
| Field | Type | Description |
|-------|------|-------------|
| `documentPath` | string | The full path of the updated document |
| `updateTime` | string | The timestamp when the document was updated |
| `documentData` | map | The current data of the document after the update (only included when `returnData` is true) |
## Data Type Format
The tool requires Firestore's native JSON format for document data. Each field must be wrapped with its type indicator:
### Basic Types
- **String**: `{"stringValue": "your string"}`
- **Integer**: `{"integerValue": "123"}` or `{"integerValue": 123}`
- **Double**: `{"doubleValue": 123.45}`
- **Boolean**: `{"booleanValue": true}`
- **Null**: `{"nullValue": null}`
- **Bytes**: `{"bytesValue": "base64EncodedString"}`
- **Timestamp**: `{"timestampValue": "2025-01-07T10:00:00Z"}` (RFC3339 format)
### Complex Types
- **GeoPoint**: `{"geoPointValue": {"latitude": 34.052235, "longitude": -118.243683}}`
- **Array**: `{"arrayValue": {"values": [{"stringValue": "item1"}, {"integerValue": "2"}]}}`
- **Map**: `{"mapValue": {"fields": {"key1": {"stringValue": "value1"}, "key2": {"booleanValue": true}}}}`
- **Reference**: `{"referenceValue": "collection/document"}`
### Special Operations
- **Delete Field**: `{"deleteValue": true}` (only works when `updateMask` is specified)
## Update Modes
### Full Document Update (Merge All)
When `updateMask` is not provided, the tool performs a merge operation that updates all fields specified in `documentData` while preserving other existing fields in the document.
### Selective Field Update
When `updateMask` is provided, only the fields listed in the mask are updated. This allows for precise control over which fields are modified, added, or deleted.
## Examples
### Basic Document Update (Full Merge)
```yaml
tools:
update-user-doc:
kind: firestore-update-document
source: my-firestore
description: Update a user document
```
Usage:
```json
{
"documentPath": "users/user123",
"documentData": {
"name": {
"stringValue": "Jane Doe"
},
"lastUpdated": {
"timestampValue": "2025-01-15T10:30:00Z"
},
"status": {
"stringValue": "active"
},
"score": {
"integerValue": "150"
}
}
}
```
### Selective Field Update with Update Mask
```json
{
"documentPath": "users/user123",
"documentData": {
"email": {
"stringValue": "newemail@example.com"
},
"profile": {
"mapValue": {
"fields": {
"bio": {
"stringValue": "Updated bio text"
},
"avatar": {
"stringValue": "https://example.com/new-avatar.jpg"
}
}
}
}
},
"updateMask": ["email", "profile.bio", "profile.avatar"]
}
```
### Update with Field Deletion
```json
{
"documentPath": "users/user123",
"documentData": {
"name": {
"stringValue": "John Smith"
},
"temporaryField": {
"deleteValue": true
},
"obsoleteData": {
"deleteValue": true
}
},
"updateMask": ["name", "temporaryField", "obsoleteData"],
"returnData": true
}
```
### Complex Update with Nested Data
```json
{
"documentPath": "companies/company456",
"documentData": {
"metadata": {
"mapValue": {
"fields": {
"lastModified": {
"timestampValue": "2025-01-15T14:30:00Z"
},
"modifiedBy": {
"stringValue": "admin@company.com"
}
}
}
},
"locations": {
"arrayValue": {
"values": [
{
"mapValue": {
"fields": {
"city": {
"stringValue": "San Francisco"
},
"coordinates": {
"geoPointValue": {
"latitude": 37.7749,
"longitude": -122.4194
}
}
}
}
},
{
"mapValue": {
"fields": {
"city": {
"stringValue": "New York"
},
"coordinates": {
"geoPointValue": {
"latitude": 40.7128,
"longitude": -74.0060
}
}
}
}
}
]
}
},
"revenue": {
"doubleValue": 5678901.23
}
},
"updateMask": ["metadata", "locations", "revenue"]
}
```
### Update with All Data Types
```json
{
"documentPath": "test-documents/doc789",
"documentData": {
"stringField": {
"stringValue": "Updated string"
},
"integerField": {
"integerValue": "999"
},
"doubleField": {
"doubleValue": 2.71828
},
"booleanField": {
"booleanValue": false
},
"nullField": {
"nullValue": null
},
"timestampField": {
"timestampValue": "2025-01-15T16:45:00Z"
},
"geoPointField": {
"geoPointValue": {
"latitude": 51.5074,
"longitude": -0.1278
}
},
"bytesField": {
"bytesValue": "VXBkYXRlZCBkYXRh"
},
"arrayField": {
"arrayValue": {
"values": [
{
"stringValue": "updated1"
},
{
"integerValue": "200"
},
{
"booleanValue": true
}
]
}
},
"mapField": {
"mapValue": {
"fields": {
"nestedString": {
"stringValue": "updated nested value"
},
"nestedNumber": {
"doubleValue": 88.88
}
}
}
},
"referenceField": {
"referenceValue": "users/updatedUser"
}
},
"returnData": true
}
```
## Authentication
The tool can be configured to require authentication:
```yaml
tools:
secure-update-doc:
kind: firestore-update-document
source: prod-firestore
description: Update documents with authentication required
authRequired:
- google-oauth
- api-key
```
## Error Handling
Common errors include:
- Document not found (when using update with a non-existent document)
- Invalid document path
- Missing or invalid document data
- Permission denied (if Firestore security rules block the operation)
- Invalid data type conversions
- Field not found in document data when using update mask
## Best Practices
1. **Use update masks for precision**: When you only need to update specific fields, use the `updateMask` parameter to avoid unintended changes
2. **Always use typed values**: Every field must be wrapped with its appropriate type indicator (e.g., `{"stringValue": "text"}`)
3. **Integer values can be strings**: The tool accepts integer values as strings (e.g., `{"integerValue": "1500"}`)
4. **Use returnData sparingly**: Only set to true when you need to verify the exact data after the update
5. **Validate data before sending**: Ensure your data matches Firestore's native JSON format
6. **Handle timestamps properly**: Use RFC3339 format for timestamp strings
7. **Base64 encode binary data**: Binary data must be base64 encoded in the `bytesValue` field
8. **Consider security rules**: Ensure your Firestore security rules allow document updates
9. **Use deleteValue for field removal**: When using update mask, set `{"deleteValue": true}` to remove fields
10. **Test with non-production data first**: Always test your updates on non-critical documents first
## Differences from Add Documents
- **Purpose**: Updates existing documents vs. creating new ones
- **Document must exist**: For standard updates (though Set with MergeAll will create if missing)
- **Update mask support**: Allows selective field updates
- **Field deletion**: Supports removing specific fields with `deleteValue`
- **Returns updateTime**: Instead of createTime
## Related Tools
- [`firestore-add-documents`](firestore-add-documents.md) - Add new documents to Firestore
- [`firestore-get-documents`](firestore-get-documents.md) - Retrieve documents by their paths
- [`firestore-query-collection`](firestore-query-collection.md) - Query documents in a collection
- [`firestore-delete-documents`](firestore-delete-documents.md) - Delete documents from Firestore

View File

@@ -21,6 +21,18 @@ tools:
5. Handle timestamps properly: Use RFC3339 format for timestamp strings
6. Base64 encode binary data: Binary data must be base64 encoded in the bytesValue field
7. Consider security rules: Ensure your Firestore security rules allow document creation in the target collection
firestore-update-document:
kind: firestore-update-document
source: firestore-source
description: |
Updates an existing document in Firestore. Supports both full document updates and selective field updates using an update mask. Please follow the best practices:
1. Use update masks for precision: When you only need to update specific fields, use the updateMask parameter to avoid unintended changes
2. Always use typed values in the documentData: Every field must be wrapped with its appropriate type indicator (e.g., {"stringValue": "text"})
3. Use deleteValue for field removal: When using update mask, set {"deleteValue": true} to remove fields
4. Integer values can be strings: The tool accepts integer values as strings (e.g., {"integerValue": "1500"})
5. Use returnData sparingly: Only set to true when you need to verify the exact data after the update
6. Handle timestamps properly: Use RFC3339 format for timestamp strings
7. Consider security rules: Ensure your Firestore security rules allow document updates
firestore-list-collections:
kind: firestore-list-collections
source: firestore-source
@@ -48,6 +60,7 @@ toolsets:
firestore-database-tools:
- firestore-get-documents
- firestore-add-documents
- firestore-update-document
- firestore-list-collections
- firestore-delete-documents
- firestore-query-collection

View File

@@ -0,0 +1,326 @@
// 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.
package firestoreupdatedocument
import (
"context"
"fmt"
"strings"
firestoreapi "cloud.google.com/go/firestore"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
firestoreds "github.com/googleapis/genai-toolbox/internal/sources/firestore"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/firestore/util"
)
const kind string = "firestore-update-document"
const documentPathKey string = "documentPath"
const documentDataKey string = "documentData"
const updateMaskKey string = "updateMask"
const returnDocumentDataKey string = "returnData"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
FirestoreClient() *firestoreapi.Client
}
// validate compatible sources are still compatible
var _ compatibleSource = &firestoreds.Source{}
var compatibleSources = [...]string{firestoreds.SourceKind}
type Config struct {
Name string `yaml:"name" validate:"required"`
Kind string `yaml:"kind" validate:"required"`
Source string `yaml:"source" validate:"required"`
Description string `yaml:"description" validate:"required"`
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
// verify source exists
rawS, ok := srcs[cfg.Source]
if !ok {
return nil, fmt.Errorf("no source named %q configured", cfg.Source)
}
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
// Create parameters
documentPathParameter := tools.NewStringParameter(
documentPathKey,
"The path of the document which needs to be updated",
)
documentDataParameter := tools.NewMapParameter(
documentDataKey,
`The document data in Firestore's native JSON format. Each field must be wrapped with a type indicator:
- Strings: {"stringValue": "text"}
- Integers: {"integerValue": "123"} or {"integerValue": 123}
- Doubles: {"doubleValue": 123.45}
- Booleans: {"booleanValue": true}
- Timestamps: {"timestampValue": "2025-01-07T10:00:00Z"}
- GeoPoints: {"geoPointValue": {"latitude": 34.05, "longitude": -118.24}}
- Arrays: {"arrayValue": {"values": [{"stringValue": "item1"}, {"integerValue": "2"}]}}
- Maps: {"mapValue": {"fields": {"key1": {"stringValue": "value1"}, "key2": {"booleanValue": true}}}}
- Null: {"nullValue": null}
- Bytes: {"bytesValue": "base64EncodedString"}
- References: {"referenceValue": "collection/document"}
- Delete field: {"deleteValue": true} (only works when updateMask is specified)`,
"", // Empty string for generic map that accepts any value type
)
updateMaskParameter := tools.NewArrayParameterWithRequired(
updateMaskKey,
"The selective fields to update. If not provided, all fields in documentData will be updated. When provided, only the specified fields will be updated, and you can use deleteValue to delete specific fields",
false, // not required
tools.NewStringParameter("field", "Field path to update. Use dot notation to access nested fields within maps (e.g., 'address.city' to update the city field within an address map, or 'user.profile.name' for deeply nested fields). Note: You cannot update individual array elements; you must update the entire array field"),
)
returnDataParameter := tools.NewBooleanParameterWithDefault(
returnDocumentDataKey,
false,
"If set to true the output will have the data of the updated document. This flag if set to false will help avoid overloading the context of the agent.",
)
parameters := tools.Parameters{
documentPathParameter,
documentDataParameter,
updateMaskParameter,
returnDataParameter,
}
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.FirestoreClient(),
manifest: tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
}
return t, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
Client *firestoreapi.Client
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) (any, error) {
mapParams := params.AsMap()
// Get document path
documentPath, ok := mapParams[documentPathKey].(string)
if !ok || documentPath == "" {
return nil, fmt.Errorf("invalid or missing '%s' parameter", documentPathKey)
}
// Get document data
documentDataRaw, ok := mapParams[documentDataKey]
if !ok {
return nil, fmt.Errorf("invalid or missing '%s' parameter", documentDataKey)
}
// Get update mask if provided
var updatePaths []string
if updateMaskRaw, ok := mapParams[updateMaskKey]; ok && updateMaskRaw != nil {
if updateMaskArray, ok := updateMaskRaw.([]any); ok {
// Use ConvertAnySliceToTyped to convert the slice
typedSlice, err := tools.ConvertAnySliceToTyped(updateMaskArray, "string")
if err != nil {
return nil, fmt.Errorf("failed to convert update mask: %w", err)
}
updatePaths, ok = typedSlice.([]string)
if !ok {
return nil, fmt.Errorf("unexpected type conversion error for update mask")
}
}
}
// Get return document data flag
returnData := false
if val, ok := mapParams[returnDocumentDataKey].(bool); ok {
returnData = val
}
// Get the document reference
docRef := t.Client.Doc(documentPath)
// Prepare update data
var writeResult *firestoreapi.WriteResult
var err error
documentData, err := util.JSONToFirestoreValue(documentDataRaw, t.Client)
if err != nil {
return nil, fmt.Errorf("failed to convert document data: %w", err)
}
if len(updatePaths) > 0 {
// Use selective field update with update mask
updates := make([]firestoreapi.Update, 0, len(updatePaths))
// Process the document data to handle special values
dataMap, err := processDocumentDataForDeleteMarkers(documentDataRaw, t.Client)
if err != nil {
return nil, fmt.Errorf("failed to process document data: %w", err)
}
for _, path := range updatePaths {
// Get the value for this path from the document data
value, exists := getFieldValue(dataMap, path)
if !exists {
return nil, fmt.Errorf("field '%s' not found in document data", path)
}
// The value will already be firestore.Delete if it was a deleteValue
updates = append(updates, firestoreapi.Update{
Path: path,
Value: value,
})
}
writeResult, err = docRef.Update(ctx, updates)
} else {
// Update all fields in the document data (merge)
writeResult, err = docRef.Set(ctx, documentData, firestoreapi.MergeAll)
}
if err != nil {
return nil, fmt.Errorf("failed to update document: %w", err)
}
// Build the response
response := map[string]any{
"documentPath": docRef.Path,
"updateTime": writeResult.UpdateTime.Format("2006-01-02T15:04:05.999999999Z"),
}
// Add document data if requested
if returnData {
// Fetch the updated document to return the current state
snapshot, err := docRef.Get(ctx)
if err != nil {
return nil, fmt.Errorf("failed to retrieve updated document: %w", err)
}
// Convert the document data to simple JSON format
simplifiedData := util.FirestoreValueToJSON(snapshot.Data())
response["documentData"] = simplifiedData
}
return response, nil
}
// processDocumentDataForDeleteMarkers processes the document data for update operations,
// handling special values like deleteValue
func processDocumentDataForDeleteMarkers(data interface{}, client *firestoreapi.Client) (map[string]interface{}, error) {
// Convert with delete support enabled
processed, err := util.JSONToFirestoreValue(data, client, true)
if err != nil {
return nil, err
}
// Ensure it's a map
if processedMap, ok := processed.(map[string]interface{}); ok {
return processedMap, nil
}
return nil, fmt.Errorf("processed data is not a map")
}
// getFieldValue retrieves a value from a nested map using a dot-separated path
func getFieldValue(data map[string]interface{}, path string) (interface{}, bool) {
// Split the path by dots for nested field access
parts := strings.Split(path, ".")
current := data
for i, part := range parts {
if i == len(parts)-1 {
// Last part - return the value
if value, exists := current[part]; exists {
return value, true
}
return nil, false
}
// Navigate deeper into the structure
if next, ok := current[part].(map[string]interface{}); ok {
current = next
} else {
return nil, false
}
}
return nil, false
}
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
return tools.ParseParams(t.Parameters, data, claims)
}
func (t Tool) Manifest() tools.Manifest {
return t.manifest
}
func (t Tool) McpManifest() tools.McpManifest {
return t.mcpManifest
}
func (t Tool) Authorized(verifiedAuthServices []string) bool {
return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
}

View File

@@ -0,0 +1,471 @@
// 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.
package firestoreupdatedocument
import (
"context"
"strings"
"testing"
yaml "github.com/goccy/go-yaml"
"github.com/google/go-cmp/cmp"
"github.com/googleapis/genai-toolbox/internal/sources"
firestoreds "github.com/googleapis/genai-toolbox/internal/sources/firestore"
"github.com/googleapis/genai-toolbox/internal/tools"
)
func TestNewConfig(t *testing.T) {
tests := []struct {
name string
yaml string
want Config
wantErr bool
}{
{
name: "valid config",
yaml: `
name: test-update-document
kind: firestore-update-document
source: test-firestore
description: Update a document in Firestore
authRequired:
- google-oauth
`,
want: Config{
Name: "test-update-document",
Kind: "firestore-update-document",
Source: "test-firestore",
Description: "Update a document in Firestore",
AuthRequired: []string{"google-oauth"},
},
wantErr: false,
},
{
name: "minimal config",
yaml: `
name: test-update-document
kind: firestore-update-document
source: test-firestore
description: Update a document
`,
want: Config{
Name: "test-update-document",
Kind: "firestore-update-document",
Source: "test-firestore",
Description: "Update a document",
},
wantErr: false,
},
{
name: "invalid yaml",
yaml: `
name: test-update-document
kind: [invalid
`,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
decoder := yaml.NewDecoder(strings.NewReader(tt.yaml))
got, err := newConfig(context.Background(), "test-update-document", decoder)
if tt.wantErr {
if err == nil {
t.Fatalf("expected error but got none")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Fatalf("config mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestConfig_ToolConfigKind(t *testing.T) {
cfg := Config{}
got := cfg.ToolConfigKind()
want := "firestore-update-document"
if got != want {
t.Fatalf("ToolConfigKind() = %v, want %v", got, want)
}
}
func TestConfig_Initialize(t *testing.T) {
tests := []struct {
name string
config Config
sources map[string]sources.Source
wantErr bool
errMsg string
}{
{
name: "valid initialization",
config: Config{
Name: "test-update-document",
Kind: "firestore-update-document",
Source: "test-firestore",
Description: "Update a document",
},
sources: map[string]sources.Source{
"test-firestore": &firestoreds.Source{},
},
wantErr: false,
},
{
name: "source not found",
config: Config{
Name: "test-update-document",
Kind: "firestore-update-document",
Source: "missing-source",
Description: "Update a document",
},
sources: map[string]sources.Source{},
wantErr: true,
errMsg: "no source named \"missing-source\" configured",
},
{
name: "incompatible source",
config: Config{
Name: "test-update-document",
Kind: "firestore-update-document",
Source: "wrong-source",
Description: "Update a document",
},
sources: map[string]sources.Source{
"wrong-source": &mockIncompatibleSource{},
},
wantErr: true,
errMsg: "invalid source for \"firestore-update-document\" tool",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tool, err := tt.config.Initialize(tt.sources)
if tt.wantErr {
if err == nil {
t.Fatalf("expected error but got none")
}
if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) {
t.Fatalf("error message %q does not contain %q", err.Error(), tt.errMsg)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if tool == nil {
t.Fatalf("expected tool to be non-nil")
}
// Verify tool properties
actualTool := tool.(Tool)
if actualTool.Name != tt.config.Name {
t.Fatalf("tool.Name = %v, want %v", actualTool.Name, tt.config.Name)
}
if actualTool.Kind != "firestore-update-document" {
t.Fatalf("tool.Kind = %v, want %v", actualTool.Kind, "firestore-update-document")
}
if diff := cmp.Diff(tt.config.AuthRequired, actualTool.AuthRequired); diff != "" {
t.Fatalf("AuthRequired mismatch (-want +got):\n%s", diff)
}
if actualTool.Parameters == nil {
t.Fatalf("expected Parameters to be non-nil")
}
if len(actualTool.Parameters) != 4 {
t.Fatalf("len(Parameters) = %v, want 4", len(actualTool.Parameters))
}
})
}
}
func TestTool_ParseParams(t *testing.T) {
tool := Tool{
Parameters: tools.Parameters{
tools.NewStringParameter("documentPath", "Document path"),
tools.NewMapParameter("documentData", "Document data", ""),
tools.NewArrayParameterWithRequired("updateMask", "Update mask", false, tools.NewStringParameter("field", "Field")),
tools.NewBooleanParameterWithDefault("returnData", false, "Return data"),
},
}
tests := []struct {
name string
data map[string]any
claims map[string]map[string]any
wantErr bool
}{
{
name: "valid params with all fields",
data: map[string]any{
"documentPath": "users/user1",
"documentData": map[string]any{
"name": map[string]any{"stringValue": "John"},
},
"updateMask": []any{"name"},
"returnData": true,
},
wantErr: false,
},
{
name: "valid params without optional fields",
data: map[string]any{
"documentPath": "users/user1",
"documentData": map[string]any{
"name": map[string]any{"stringValue": "John"},
},
},
wantErr: false,
},
{
name: "missing required documentPath",
data: map[string]any{
"documentData": map[string]any{
"name": map[string]any{"stringValue": "John"},
},
},
wantErr: true,
},
{
name: "missing required documentData",
data: map[string]any{
"documentPath": "users/user1",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
params, err := tool.ParseParams(tt.data, tt.claims)
if tt.wantErr {
if err == nil {
t.Fatalf("expected error but got none")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if params == nil {
t.Fatalf("expected params to be non-nil")
}
})
}
}
func TestTool_Manifest(t *testing.T) {
tool := Tool{
manifest: tools.Manifest{
Description: "Test description",
Parameters: []tools.ParameterManifest{
{
Name: "documentPath",
Type: "string",
Description: "Document path",
Required: true,
},
},
AuthRequired: []string{"google-oauth"},
},
}
manifest := tool.Manifest()
if manifest.Description != "Test description" {
t.Fatalf("manifest.Description = %v, want %v", manifest.Description, "Test description")
}
if len(manifest.Parameters) != 1 {
t.Fatalf("len(manifest.Parameters) = %v, want 1", len(manifest.Parameters))
}
if diff := cmp.Diff([]string{"google-oauth"}, manifest.AuthRequired); diff != "" {
t.Fatalf("AuthRequired mismatch (-want +got):\n%s", diff)
}
}
func TestTool_McpManifest(t *testing.T) {
tool := Tool{
mcpManifest: tools.McpManifest{
Name: "test-update-document",
Description: "Test description",
InputSchema: tools.McpToolsSchema{
Type: "object",
Properties: map[string]tools.ParameterMcpManifest{
"documentPath": {
Type: "string",
Description: "Document path",
},
},
Required: []string{"documentPath"},
},
},
}
mcpManifest := tool.McpManifest()
if mcpManifest.Name != "test-update-document" {
t.Fatalf("mcpManifest.Name = %v, want %v", mcpManifest.Name, "test-update-document")
}
if mcpManifest.Description != "Test description" {
t.Fatalf("mcpManifest.Description = %v, want %v", mcpManifest.Description, "Test description")
}
if mcpManifest.InputSchema.Type == "" {
t.Fatalf("expected InputSchema to be non-empty")
}
}
func TestTool_Authorized(t *testing.T) {
tests := []struct {
name string
authRequired []string
verifiedAuthServices []string
want bool
}{
{
name: "no auth required",
authRequired: nil,
verifiedAuthServices: nil,
want: true,
},
{
name: "auth required and provided",
authRequired: []string{"google-oauth"},
verifiedAuthServices: []string{"google-oauth"},
want: true,
},
{
name: "auth required but not provided",
authRequired: []string{"google-oauth"},
verifiedAuthServices: []string{"api-key"},
want: false,
},
{
name: "multiple auth required, one provided",
authRequired: []string{"google-oauth", "api-key"},
verifiedAuthServices: []string{"google-oauth"},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tool := Tool{
AuthRequired: tt.authRequired,
}
got := tool.Authorized(tt.verifiedAuthServices)
if got != tt.want {
t.Fatalf("Authorized() = %v, want %v", got, tt.want)
}
})
}
}
func TestGetFieldValue(t *testing.T) {
tests := []struct {
name string
data map[string]interface{}
path string
want interface{}
exists bool
}{
{
name: "simple field",
data: map[string]interface{}{
"name": "John",
},
path: "name",
want: "John",
exists: true,
},
{
name: "nested field",
data: map[string]interface{}{
"user": map[string]interface{}{
"name": "John",
},
},
path: "user.name",
want: "John",
exists: true,
},
{
name: "deeply nested field",
data: map[string]interface{}{
"level1": map[string]interface{}{
"level2": map[string]interface{}{
"level3": "value",
},
},
},
path: "level1.level2.level3",
want: "value",
exists: true,
},
{
name: "non-existent field",
data: map[string]interface{}{
"name": "John",
},
path: "age",
want: nil,
exists: false,
},
{
name: "non-existent nested field",
data: map[string]interface{}{
"user": map[string]interface{}{
"name": "John",
},
},
path: "user.age",
want: nil,
exists: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, exists := getFieldValue(tt.data, tt.path)
if exists != tt.exists {
t.Fatalf("exists = %v, want %v", exists, tt.exists)
}
if tt.exists {
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Fatalf("value mismatch (-want +got):\n%s", diff)
}
}
})
}
}
// mockIncompatibleSource is a mock source that doesn't implement compatibleSource
type mockIncompatibleSource struct{}
func (m *mockIncompatibleSource) SourceKind() string {
return "mock"
}

View File

@@ -28,17 +28,33 @@ import (
// JSONToFirestoreValue converts a JSON value with type information to a Firestore-compatible value
// The input should be a map with a single key indicating the type (e.g., "stringValue", "integerValue")
// If a client is provided, referenceValue types will be converted to *firestore.DocumentRef
func JSONToFirestoreValue(value interface{}, client *firestore.Client) (interface{}, error) {
// If allowDelete is true, deleteValue markers will be converted to firestore.Delete
func JSONToFirestoreValue(value interface{}, client *firestore.Client, allowDelete ...bool) (interface{}, error) {
if value == nil {
return nil, nil
}
// Check if delete values are allowed (optional parameter)
allowDeleteValue := false
if len(allowDelete) > 0 {
allowDeleteValue = allowDelete[0]
}
switch v := value.(type) {
case map[string]interface{}:
// Check for typed values
if len(v) == 1 {
for key, val := range v {
switch key {
case "deleteValue":
// Handle delete marker if allowed
if allowDeleteValue {
if b, ok := val.(bool); ok && b {
return firestore.Delete, nil
}
}
// If not allowed or not true, treat as regular map
return convertPlainMap(v, client, allowDeleteValue)
case "nullValue":
return nil, nil
case "booleanValue":
@@ -109,7 +125,7 @@ func JSONToFirestoreValue(value interface{}, client *firestore.Client) (interfac
if values, ok := arrayMap["values"].([]interface{}); ok {
result := make([]interface{}, len(values))
for i, item := range values {
converted, err := JSONToFirestoreValue(item, client)
converted, err := JSONToFirestoreValue(item, client, allowDeleteValue)
if err != nil {
return nil, fmt.Errorf("array item %d: %w", i, err)
}
@@ -125,7 +141,7 @@ func JSONToFirestoreValue(value interface{}, client *firestore.Client) (interfac
if fields, ok := mapMap["fields"].(map[string]interface{}); ok {
result := make(map[string]interface{})
for k, v := range fields {
converted, err := JSONToFirestoreValue(v, client)
converted, err := JSONToFirestoreValue(v, client, allowDeleteValue)
if err != nil {
return nil, fmt.Errorf("map field %q: %w", k, err)
}
@@ -147,12 +163,12 @@ func JSONToFirestoreValue(value interface{}, client *firestore.Client) (interfac
return nil, fmt.Errorf("reference value must be a string")
default:
// If not a typed value, treat as regular map
return convertPlainMap(v, client)
return convertPlainMap(v, client, allowDeleteValue)
}
}
}
// Regular map without type annotation
return convertPlainMap(v, client)
return convertPlainMap(v, client, allowDeleteValue)
default:
// Plain values (for backward compatibility)
return value, nil
@@ -160,10 +176,10 @@ func JSONToFirestoreValue(value interface{}, client *firestore.Client) (interfac
}
// convertPlainMap converts a plain map to Firestore format
func convertPlainMap(m map[string]interface{}, client *firestore.Client) (map[string]interface{}, error) {
func convertPlainMap(m map[string]interface{}, client *firestore.Client, allowDelete bool) (map[string]interface{}, error) {
result := make(map[string]interface{})
for k, v := range m {
converted, err := JSONToFirestoreValue(v, client)
converted, err := JSONToFirestoreValue(v, client, allowDelete)
if err != nil {
return nil, fmt.Errorf("field %q: %w", k, err)
}

View File

@@ -133,6 +133,7 @@ func TestFirestoreToolEndpoints(t *testing.T) {
runFirestoreQueryCollectionTest(t, testCollectionName)
runFirestoreListCollectionsTest(t, testCollectionName, testSubCollectionName, docPath1)
runFirestoreAddDocumentsTest(t, testCollectionName)
runFirestoreUpdateDocumentTest(t, testCollectionName, testDocID1)
runFirestoreDeleteDocumentsTest(t, docPath3)
runFirestoreGetRulesTest(t)
runFirestoreValidateRulesTest(t)
@@ -576,6 +577,11 @@ func getFirestoreToolsConfig(sourceConfig map[string]any) map[string]any {
"source": "my-instance",
"description": "Add documents to Firestore",
},
"firestore-update-doc": map[string]any{
"kind": "firestore-update-document",
"source": "my-instance",
"description": "Update a document in Firestore",
},
}
return map[string]any{
@@ -584,6 +590,238 @@ func getFirestoreToolsConfig(sourceConfig map[string]any) map[string]any {
}
}
func runFirestoreUpdateDocumentTest(t *testing.T, collectionName string, docID string) {
docPath := fmt.Sprintf("%s/%s", collectionName, docID)
invokeTcs := []struct {
name string
api string
requestBody io.Reader
wantKeys []string
validateContent bool
expectedContent map[string]interface{}
isErr bool
}{
{
name: "update document with simple fields",
api: "http://127.0.0.1:5000/api/tool/firestore-update-doc/invoke",
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
"documentPath": "%s",
"documentData": {
"name": {"stringValue": "Alice Updated"},
"status": {"stringValue": "active"}
}
}`, docPath))),
wantKeys: []string{"documentPath", "updateTime"},
isErr: false,
},
{
name: "update document with selective fields using updateMask",
api: "http://127.0.0.1:5000/api/tool/firestore-update-doc/invoke",
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
"documentPath": "%s",
"documentData": {
"age": {"integerValue": "31"},
"email": {"stringValue": "alice@example.com"}
},
"updateMask": ["age"]
}`, docPath))),
wantKeys: []string{"documentPath", "updateTime"},
isErr: false,
},
{
name: "update document with field deletion",
api: "http://127.0.0.1:5000/api/tool/firestore-update-doc/invoke",
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
"documentPath": "%s",
"documentData": {
"name": {"stringValue": "Alice Final"},
"status": {"deleteValue": true}
},
"updateMask": ["name", "status"]
}`, docPath))),
wantKeys: []string{"documentPath", "updateTime"},
isErr: false,
},
{
name: "update document with complex types",
api: "http://127.0.0.1:5000/api/tool/firestore-update-doc/invoke",
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
"documentPath": "%s",
"documentData": {
"location": {
"geoPointValue": {
"latitude": 40.7128,
"longitude": -74.0060
}
},
"tags": {
"arrayValue": {
"values": [
{"stringValue": "updated"},
{"stringValue": "test"}
]
}
},
"metadata": {
"mapValue": {
"fields": {
"lastModified": {"timestampValue": "2025-01-15T10:00:00Z"},
"version": {"integerValue": "2"}
}
}
}
}
}`, docPath))),
wantKeys: []string{"documentPath", "updateTime"},
isErr: false,
},
{
name: "update document with returnData",
api: "http://127.0.0.1:5000/api/tool/firestore-update-doc/invoke",
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
"documentPath": "%s",
"documentData": {
"testField": {"stringValue": "test value"},
"testNumber": {"integerValue": "42"}
},
"returnData": true
}`, docPath))),
wantKeys: []string{"documentPath", "updateTime", "documentData"},
validateContent: true,
expectedContent: map[string]interface{}{
"testField": "test value",
"testNumber": float64(42), // JSON numbers are decoded as float64
},
isErr: false,
},
{
name: "update nested fields with updateMask",
api: "http://127.0.0.1:5000/api/tool/firestore-update-doc/invoke",
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
"documentPath": "%s",
"documentData": {
"profile": {
"mapValue": {
"fields": {
"bio": {"stringValue": "Updated bio"},
"avatar": {"stringValue": "avatar.jpg"}
}
}
}
},
"updateMask": ["profile.bio", "profile.avatar"]
}`, docPath))),
wantKeys: []string{"documentPath", "updateTime"},
isErr: false,
},
{
name: "missing documentPath parameter",
api: "http://127.0.0.1:5000/api/tool/firestore-update-doc/invoke",
requestBody: bytes.NewBuffer([]byte(`{"documentData": {"test": {"stringValue": "value"}}}`)),
isErr: true,
},
{
name: "missing documentData parameter",
api: "http://127.0.0.1:5000/api/tool/firestore-update-doc/invoke",
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"documentPath": "%s"}`, docPath))),
isErr: true,
},
{
name: "update non-existent document",
api: "http://127.0.0.1:5000/api/tool/firestore-update-doc/invoke",
requestBody: bytes.NewBuffer([]byte(`{
"documentPath": "non-existent-collection/non-existent-doc",
"documentData": {
"field": {"stringValue": "value"}
}
}`)),
wantKeys: []string{"documentPath", "updateTime"}, // Set with MergeAll creates if doesn't exist
isErr: false,
},
{
name: "invalid field in updateMask",
api: "http://127.0.0.1:5000/api/tool/firestore-update-doc/invoke",
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{
"documentPath": "%s",
"documentData": {
"field1": {"stringValue": "value1"}
},
"updateMask": ["field1", "nonExistentField"]
}`, docPath))),
isErr: true, // Should fail because nonExistentField is not in documentData
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
req, err := http.NewRequest(http.MethodPost, tc.api, tc.requestBody)
if err != nil {
t.Fatalf("unable to create request: %s", err)
}
req.Header.Add("Content-type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("unable to send request: %s", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
if tc.isErr {
return
}
bodyBytes, _ := io.ReadAll(resp.Body)
t.Fatalf("response status code is not 200, got %d: %s", resp.StatusCode, string(bodyBytes))
}
var body map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&body)
if err != nil {
t.Fatalf("error parsing response body: %v", err)
}
got, ok := body["result"].(string)
if !ok {
t.Fatalf("unable to find result in response body")
}
// Parse the result string as JSON
var resultJSON map[string]interface{}
err = json.Unmarshal([]byte(got), &resultJSON)
if err != nil {
t.Fatalf("error parsing result as JSON: %v", err)
}
// Check if all wanted keys exist
for _, key := range tc.wantKeys {
if _, exists := resultJSON[key]; !exists {
t.Fatalf("expected key %q not found in result: %s", key, got)
}
}
// Validate document data if required
if tc.validateContent {
docData, ok := resultJSON["documentData"].(map[string]interface{})
if !ok {
t.Fatalf("documentData is not a map: %v", resultJSON["documentData"])
}
// Check that expected fields are present with correct values
for key, expectedValue := range tc.expectedContent {
actualValue, exists := docData[key]
if !exists {
t.Fatalf("expected field %q not found in documentData", key)
}
if actualValue != expectedValue {
t.Fatalf("field %q mismatch: expected %v, got %v", key, expectedValue, actualValue)
}
}
}
})
}
}
func runFirestoreAddDocumentsTest(t *testing.T, collectionName string) {
invokeTcs := []struct {
name string