Compare commits

..

1 Commits

Author SHA1 Message Date
duwenxin99
0fb1235cb0 draft 2025-06-12 16:38:16 -04:00
62 changed files with 1996 additions and 1704 deletions

View File

@@ -17,8 +17,15 @@ steps:
waitFor: ['-']
script: |
#!/usr/bin/env bash
docker buildx create --name container-builder --driver docker-container --bootstrap --use
docker buildx build --platform linux/amd64,linux/arm64 --build-arg COMMIT_SHA=$(git rev-parse HEAD) -t ${_DOCKER_URI}:$REF_NAME --push .
docker buildx build --build-arg COMMIT_SHA=$(git rev-parse HEAD) -t ${_DOCKER_URI}:$REF_NAME .
- id: "push-docker"
name: "gcr.io/cloud-builders/docker"
waitFor:
- "build-docker"
script: |
#!/usr/bin/env bash
docker push ${_DOCKER_URI}:$REF_NAME
- id: "install-dependencies"
name: golang:1
@@ -123,7 +130,6 @@ options:
automapSubstitutions: true
dynamicSubstitutions: true
logging: CLOUD_LOGGING_ONLY # Necessary for custom service account
machineType: 'E2_HIGHCPU_32'
substitutions:
_REGION: us-central1

View File

@@ -33,7 +33,7 @@ steps:
- name: "go"
path: "/gopath"
script: |
go test -c -race ./tests/...
go test -c -cover -coverprofile=coverage.out -race ./tests/...
- id: "cloud-sql-pg"
name: golang:1
@@ -75,7 +75,7 @@ steps:
args:
- -c
- |
./alloydbpg.test -test.v
./alloydbpg.test -test.v -test.coverprofile=coverage.out
- id: "alloydb-ai-nl"
name: golang:1

View File

@@ -18,13 +18,19 @@ steps:
script: |
#!/usr/bin/env bash
export VERSION=$(cat ./cmd/version.txt)
docker buildx create --name container-builder --driver docker-container --bootstrap --use
docker buildx build --build-arg BUILD_TYPE=container.release --build-arg COMMIT_SHA=$(git rev-parse HEAD) -t ${_DOCKER_URI}:$VERSION -t ${_DOCKER_URI}:latest .
export TAGS="-t ${_DOCKER_URI}:$VERSION"
- id: "push-docker"
name: "gcr.io/cloud-builders/docker"
waitFor:
- "build-docker"
script: |
#!/usr/bin/env bash
export VERSION=$(cat ./cmd/version.txt)
docker push ${_DOCKER_URI}:$VERSION
if [[ $_PUSH_LATEST == 'true' ]]; then
export TAGS="$TAGS -t ${_DOCKER_URI}:latest"
docker push ${_DOCKER_URI}:latest
fi
docker buildx build --platform linux/amd64,linux/arm64 --build-arg BUILD_TYPE=container.release --build-arg COMMIT_SHA=$(git rev-parse HEAD) $TAGS --push .
- id: "install-dependencies"
name: golang:1
@@ -137,7 +143,6 @@ options:
automapSubstitutions: true
dynamicSubstitutions: true
logging: CLOUD_LOGGING_ONLY # Necessary for custom service account
machineType: 'E2_HIGHCPU_32'
substitutions:
_REGION: us-central1

View File

@@ -2,14 +2,7 @@ assign_issues:
- kurtisvg
- Yuan325
- duwenxin99
assign_issues_by:
- labels:
- 'product: bigquery'
to:
- Genesis929
- shobsi
- jiaxunwu
assign_prs:
- kurtisvg
- Yuan325
- duwenxin99
- duwenxin99

6
.github/labels.yaml vendored
View File

@@ -84,9 +84,3 @@
- name: 'status: awaiting codeowners'
color: 8befd7
description: Status - Awaiting response from code owners.
# Product Labels
- name: 'product: bigquery'
color: 5065c7
description: Product - Assigned to the BigQuery team.

View File

@@ -31,8 +31,6 @@ branchProtectionRules:
- "header-check"
# - Add required status checks like presubmit tests
- "unit tests (ubuntu-latest)"
- "unit tests (windows-latest)"
- "unit tests (macos-latest)"
- "integration-test-pr (toolbox-testing-438616)"
requiredApprovingReviewCount: 1
requiresCodeOwnerReviews: true

View File

@@ -32,7 +32,8 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
# os: [macos-latest, windows-latest, ubuntu-latest]
os: [ubuntu-latest]
fail-fast: false
permissions:
contents: 'read'
@@ -74,8 +75,7 @@ jobs:
- name: Build
run: go build -v ./...
- name: Run tests with coverage
if: ${{ runner.os == 'Linux' }}
- name: Run tests
run: |
source_dir="./internal/sources/*"
tool_dir="./internal/tools/*"
@@ -85,13 +85,7 @@ jobs:
go test -race -cover -coverprofile=coverage.out -v $included_packages
go test -race -v ./internal/sources/... ./internal/tools/... ./internal/auth/...
- name: Run tests without coverage
if: ${{ runner.os != 'Linux' }}
run: |
go test -race -v ./internal/... ./cmd/...
- name: Check coverage
if: ${{ runner.os == 'Linux' }}
run: |
FILE_TO_EXCLUDE="github.com/googleapis/genai-toolbox/internal/server/config.go"
ESCAPED_PATH=$(echo "$FILE_TO_EXCLUDE" | sed 's/\//\\\//g; s/\./\\\./g')

View File

@@ -41,48 +41,6 @@
### Testing
#### Writing Tests
New contributions should be added with both unit tests and integration tests.
- Unit tests are in the same package as the source code.
- Integration tests are in the `/tests` directory, under its dedicated package
named after its source. We have several pre-defined integration test suites
in the `/tests/tool.go` that are **required** to be run as long as your code contains related
features:
1. [RunToolGetTest][tool-get]: tests for the `GET` endpoint that returns the
tool's manifest.
2. [RunToolInvokeTest][tool-call]: tests for tool calling through the native
Toolbox endpoints.
3. [RunMCPToolCallMethod][mcp-call]: tests tool calling through the MCP
endpoints.
4. (Optional) [RunExecuteSqlToolInvokeTest][execute-sql]: tests an
`execute-sql` tool for any source. Only run this test if you are adding an
`execute-sql` tool.
5. (Optional) [RunToolInvokeWithTemplateParameters][temp-param]: tests for [template
parameters][temp-param-doc]. Only run this test if template parameters apply to your tool.
[tool-get]:
https://github.com/googleapis/genai-toolbox/blob/fd300dc606d88bf9f7bba689e2cee4e3565537dd/tests/tool.go#L31
[tool-call]:
<https://github.com/googleapis/genai-toolbox/blob/fd300dc606d88bf9f7bba689e2cee4e3565537dd/tests/tool.go#L79>
[mcp-call]:
https://github.com/googleapis/genai-toolbox/blob/fd300dc606d88bf9f7bba689e2cee4e3565537dd/tests/tool.go#L554
[execute-sql]:
<https://github.com/googleapis/genai-toolbox/blob/fd300dc606d88bf9f7bba689e2cee4e3565537dd/tests/tool.go#L431>
[temp-param]:
<https://github.com/googleapis/genai-toolbox/blob/fd300dc606d88bf9f7bba689e2cee4e3565537dd/tests/tool.go#L297>
[temp-param-doc]:
https://googleapis.github.io/genai-toolbox/resources/tools/#template-parameters
#### Running Tests
- Run the lint check:
```bash

View File

@@ -13,7 +13,7 @@
# limitations under the License.
# Use the latest stable golang 1.x to compile to a binary
FROM --platform=$BUILDPLATFORM golang:1 AS build
FROM --platform=$BUILDPLATFORM golang:1 as build
WORKDIR /go/src/genai-toolbox
COPY . .

View File

@@ -284,7 +284,7 @@ func run(cmd *Command) error {
ctx = util.WithLogger(ctx, cmd.logger)
// Set up OpenTelemetry
otelShutdown, err := telemetry.SetupOTel(ctx, cmd.cfg.Version, cmd.cfg.TelemetryOTLP, cmd.cfg.TelemetryGCP, cmd.cfg.TelemetryServiceName)
otelShutdown, err := telemetry.SetupOTel(ctx, cmd.Version, cmd.cfg.TelemetryOTLP, cmd.cfg.TelemetryGCP, cmd.cfg.TelemetryServiceName)
if err != nil {
errMsg := fmt.Errorf("error setting up OpenTelemetry: %w", err)
cmd.logger.ErrorContext(ctx, errMsg.Error())
@@ -352,33 +352,30 @@ func run(cmd *Command) error {
return errMsg
}
err = s.Listen(ctx)
if err != nil {
errMsg := fmt.Errorf("toolbox failed to start listener: %w", err)
cmd.logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
cmd.logger.InfoContext(ctx, "Server ready to serve!")
// run server in background
srvErr := make(chan error)
if cmd.cfg.Stdio {
go func() {
defer close(srvErr)
go func() {
defer close(srvErr)
if cmd.cfg.Stdio {
err = s.ServeStdio(ctx, cmd.inStream, cmd.outStream)
if err != nil {
srvErr <- err
}
}()
} else {
err = s.Listen(ctx)
if err != nil {
errMsg := fmt.Errorf("toolbox failed to start listener: %w", err)
cmd.logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
cmd.logger.InfoContext(ctx, "Server ready to serve!")
go func() {
defer close(srvErr)
} else {
err = s.Serve(ctx)
if err != nil {
srvErr <- err
}
}()
}
}
}()
// wait for either the server to error out or the command's context to be canceled
select {

View File

@@ -1,23 +0,0 @@
---
title: "Blogs"
type: docs
weight: 3
description: Toolbox Tuesday blog posts.
---
## Toolbox Tuesday Blogs
Every Tuesday, we post a blog on our [Medium page][medium]. These blogs will be covering
topics, tutorials, or samples that are related to developing with Toolbox.
Follow us and stay tuned!
## Previous Blogs
- [Powering your IDEs build-time agents with MCP Toolbox for Databases][blog1]
- [Using HTTP endpoints as tools with MCP Toolbox for Databases][blog2]
[medium]: https://medium.com/@mcp_toolbox
[blog1]:
https://medium.com/google-cloud/powering-your-ides-build-time-agents-with-mcp-toolbox-for-databases-123f0d837804
[blog2]:
https://medium.com/google-cloud/using-http-endpoints-as-tools-with-mcp-toolbox-for-databases-e93ab75b60cd

View File

@@ -1,22 +0,0 @@
---
title: "Contact Us"
type: docs
weight: 10
description: Contact the Toolbox team.
---
Toolbox is an open source project and we welcome contributions and discussion from the
developer community.
## How to Get in Touch
- If you want to make a contribution, [open an issue][issue] first so that we can agree
on the general design.
- If you found a bug or have ideas for improvements, [open an issue][issue] and
we will reply as soon as possible.
- For any topics, join our [discord server][discord], where we can discuss things in real-time for a faster response.
[issue]: https://github.com/googleapis/genai-toolbox/issues
[discord]: https://discord.com/invite/Dmm69peqjh

View File

@@ -5,9 +5,266 @@ weight: 2
description: >
Connect your IDE to AlloyDB using Toolbox.
---
<html>
<head>
<link rel="canonical" href="https://cloud.google.com/alloydb/docs/pre-built-tools-with-mcp-toolbox"/>
<meta http-equiv="refresh" content="0;url=https://cloud.google.com/alloydb/docs/pre-built-tools-with-mcp-toolbox"/>
</head>
</html>
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol for connecting Large Language Models (LLMs) to data sources like AlloyDB. This guide covers how to use [MCP Toolbox for Databases][toolbox] to expose your developer assistant tools to a AlloyDB for Postgres instance:
* [Cursor][cursor]
* [Windsurf][windsurf] (Codium)
* [Visual Studio Code ][vscode] (Copilot)
* [Cline][cline] (VS Code extension)
* [Claude desktop][claudedesktop]
* [Claude code][claudecode]
[toolbox]: https://github.com/googleapis/genai-toolbox
[cursor]: #configure-your-mcp-client
[windsurf]: #configure-your-mcp-client
[vscode]: #configure-your-mcp-client
[cline]: #configure-your-mcp-client
[claudedesktop]: #configure-your-mcp-client
[claudecode]: #configure-your-mcp-client
## Before you begin
1. In the Google Cloud console, on the [project selector page](https://console.cloud.google.com/projectselector2/home/dashboard), select or create a Google Cloud project.
1. [Make sure that billing is enabled for your Google Cloud project](https://cloud.google.com/billing/docs/how-to/verify-billing-enabled#confirm_billing_is_enabled_on_a_project).
## Set up the database
1. [Enable the AlloyDB, Compute Engine, Cloud Resource Manager, and Service Networking APIs in the Google Cloud project](https://console.cloud.google.com/flows/enableapi?apiid=alloydb.googleapis.com,compute.googleapis.com,cloudresourcemanager.googleapis.com,servicenetworking.googleapis.com).
1. [Create a cluster and its primary instance](https://cloud.google.com/alloydb/docs/quickstart/create-and-connect). These instructions assume that your AlloyDB instance has a [public IP address](https://cloud.google.com/alloydb/docs/connect-public-ip). By default, AlloyDB assigns a private IP address to a new instance. Toolbox will connect securely using the [AlloyDB Language Connectors](https://cloud.google.com/alloydb/docs/language-connectors-overview).
1. Configure the required roles and permissions to complete this task. You will need [Cloud AlloyDB Client](https://cloud.google.com/alloydb/docs/auth-proxy/connect#required-iam-permissions) (`roles/alloydb.client`) and Service Usage Consumer (`roles/serviceusage.serviceUsageConsumer`) roles or equivalent IAM permissions to connect to the instance.
1. Configured [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment) for your environment.
1. Create or reuse [a database user](https://cloud.google.com/alloydb/docs/database-users/manage-roles) and have the username and password ready.
## Install MCP Toolbox
1. Download the latest version of Toolbox as a binary. Select the [correct binary](https://github.com/googleapis/genai-toolbox/releases) corresponding to your OS and CPU architecture. You are required to use Toolbox version V0.6.0+:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/windows/amd64/toolbox
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->
1. Make the binary executable:
```bash
chmod +x toolbox
```
1. Verify the installation:
```bash
./toolbox --version
```
## Configure your MCP Client
{{< tabpane text=true >}}
{{% tab header="Claude code" lang="en" %}}
1. Install [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
1. Create a `.mcp.json` file in your project root if it doesn't exist.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"alloydb": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","alloydb-postgres","--stdio"],
"env": {
"ALLOYDB_POSTGRES_PROJECT": "your-gcp-project-id",
"ALLOYDB_POSTGRES_REGION": "your-cluster-region",
"ALLOYDB_POSTGRES_CLUSTER": "your-cluster-name",
"ALLOYDB_POSTGRES_INSTANCE": "your-instance-name",
"ALLOYDB_POSTGRES_DATABASE": "your-database-name",
"ALLOYDB_POSTGRES_USER": "your-database-user",
"ALLOYDB_POSTGRES_PASSWORD": "your-database-password"
}
}
}
}
```
1. Restart Claude code to apply the new configuration.
{{% /tab %}}
{{% tab header="Claude desktop" lang="en" %}}
1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
1. Under the Developer tab, tap Edit Config to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"alloydb": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","alloydb-postgres","--stdio"],
"env": {
"ALLOYDB_POSTGRES_PROJECT": "your-gcp-project-id",
"ALLOYDB_POSTGRES_REGION": "your-cluster-region",
"ALLOYDB_POSTGRES_CLUSTER": "your-cluster-name",
"ALLOYDB_POSTGRES_INSTANCE": "your-instance-name",
"ALLOYDB_POSTGRES_DATABASE": "your-database-name",
"ALLOYDB_POSTGRES_USER": "your-database-user",
"ALLOYDB_POSTGRES_PASSWORD": "your-database-password"
}
}
}
}
```
1. Restart Claude desktop.
1. From the new chat screen, you should see a hammer (MCP) icon appear with the new MCP server available.
{{% /tab %}}
{{% tab header="Cline" lang="en" %}}
1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and tap the **MCP Servers** icon.
1. Tap Configure MCP Servers to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"alloydb": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","alloydb-postgres","--stdio"],
"env": {
"ALLOYDB_POSTGRES_PROJECT": "your-gcp-project-id",
"ALLOYDB_POSTGRES_REGION": "your-cluster-region",
"ALLOYDB_POSTGRES_CLUSTER": "your-cluster-name",
"ALLOYDB_POSTGRES_INSTANCE": "your-instance-name",
"ALLOYDB_POSTGRES_DATABASE": "your-database-name",
"ALLOYDB_POSTGRES_USER": "your-database-user",
"ALLOYDB_POSTGRES_PASSWORD": "your-database-password"
}
}
}
}
```
1. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Cursor" lang="en" %}}
1. Create a `.cursor` directory in your project root if it doesn't exist.
1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"alloydb": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","alloydb-postgres","--stdio"],
"env": {
"ALLOYDB_POSTGRES_PROJECT": "your-gcp-project-id",
"ALLOYDB_POSTGRES_REGION": "your-cluster-region",
"ALLOYDB_POSTGRES_CLUSTER": "your-cluster-name",
"ALLOYDB_POSTGRES_INSTANCE": "your-instance-name",
"ALLOYDB_POSTGRES_DATABASE": "your-database-name",
"ALLOYDB_POSTGRES_USER": "your-database-user",
"ALLOYDB_POSTGRES_PASSWORD": "your-database-password"
}
}
}
}
```
1. [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor Settings > MCP**. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and create a `.vscode` directory in your project root if it doesn't exist.
1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"alloydb": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","alloydb-postgres","--stdio"],
"env": {
"ALLOYDB_POSTGRES_PROJECT": "your-gcp-project-id",
"ALLOYDB_POSTGRES_REGION": "your-cluster-region",
"ALLOYDB_POSTGRES_CLUSTER": "your-cluster-name",
"ALLOYDB_POSTGRES_INSTANCE": "your-instance-name",
"ALLOYDB_POSTGRES_DATABASE": "your-database-name",
"ALLOYDB_POSTGRES_USER": "your-database-user",
"ALLOYDB_POSTGRES_PASSWORD": "your-database-password"
}
}
}
}
```
{{% /tab %}}
{{% tab header="Windsurf" lang="en" %}}
1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the Cascade assistant.
1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"alloydb": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","alloydb-postgres","--stdio"],
"env": {
"ALLOYDB_POSTGRES_PROJECT": "your-gcp-project-id",
"ALLOYDB_POSTGRES_REGION": "your-cluster-region",
"ALLOYDB_POSTGRES_CLUSTER": "your-cluster-name",
"ALLOYDB_POSTGRES_INSTANCE": "your-instance-name",
"ALLOYDB_POSTGRES_DATABASE": "your-database-name",
"ALLOYDB_POSTGRES_USER": "your-database-user",
"ALLOYDB_POSTGRES_PASSWORD": "your-database-password"
}
}
}
}
```
{{% /tab %}}
{{< /tabpane >}}
## Use Tools
Your AI tool is now connected to AlloyDB using MCP. Try asking your AI assistant to list tables, create a table, or define and execute other SQL statements.
The following tools are available to the LLM:
1. **list_tables**: lists tables and descriptions
1. **execute_sql**: execute any SQL statement
{{< notice note >}}
Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs will adapt to the tools available, so this shouldn't affect most users.
{{< /notice >}}

View File

@@ -5,9 +5,235 @@ weight: 2
description: >
Connect your IDE to BigQuery using Toolbox.
---
<html>
<head>
<link rel="canonical" href="https://cloud.google.com/bigquery/docs/pre-built-tools-with-mcp-toolbox"/>
<meta http-equiv="refresh" content="0;url=https://cloud.google.com/bigquery/docs/pre-built-tools-with-mcp-toolbox"/>
</head>
</html>
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol for connecting Large Language Models (LLMs) to data sources like BigQuery. This guide covers how to use [MCP Toolbox for Databases][toolbox] to expose your developer assistant tools to a BigQuery instance:
* [Cursor][cursor]
* [Windsurf][windsurf] (Codium)
* [Visual Studio Code ][vscode] (Copilot)
* [Cline][cline] (VS Code extension)
* [Claude desktop][claudedesktop]
* [Claude code][claudecode]
[toolbox]: https://github.com/googleapis/genai-toolbox
[cursor]: #configure-your-mcp-client
[windsurf]: #configure-your-mcp-client
[vscode]: #configure-your-mcp-client
[cline]: #configure-your-mcp-client
[claudedesktop]: #configure-your-mcp-client
[claudecode]: #configure-your-mcp-client
## Before you begin
1. In the Google Cloud console, on the [project selector page](https://console.cloud.google.com/projectselector2/home/dashboard), select or create a Google Cloud project.
1. [Make sure that billing is enabled for your Google Cloud project](https://cloud.google.com/billing/docs/how-to/verify-billing-enabled#confirm_billing_is_enabled_on_a_project).
## Set up the database
1. [Enable the BigQuery API in the Google Cloud project](https://console.cloud.google.com/flows/enableapi?apiid=bigquery.googleapis.com&redirect=https://console.cloud.google.com).
1. Configure the required roles and permissions to complete this task. You will need [BigQuery User](https://cloud.google.com/bigquery/docs/access-control) role (`roles/bigquery.user`), BigQuery Data Viewer role(`roles/bigquery.dataViewer`), or equivalent IAM permissions to connect to the instance.
1. Configured [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment) for your environment.
## Install MCP Toolbox
1. Download the latest version of Toolbox as a binary. Select the [correct binary](https://github.com/googleapis/genai-toolbox/releases) corresponding to your OS and CPU architecture. You are required to use Toolbox version V0.6.0+:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/windows/amd64/toolbox
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->
1. Make the binary executable:
```bash
chmod +x toolbox
```
1. Verify the installation:
```bash
./toolbox --version
```
## Configure your MCP Client
{{< tabpane text=true >}}
{{% tab header="Claude code" lang="en" %}}
1. Install [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
1. Create a `.mcp.json` file in your project root if it doesn't exist.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"bigquery": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","bigquery","--stdio"],
"env": {
"BIGQUERY_PROJECT": ""
}
}
}
}
```
1. Restart Claude code to apply the new configuration.
{{% /tab %}}
{{% tab header="Claude desktop" lang="en" %}}
1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
1. Under the Developer tab, tap Edit Config to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"bigquery": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","bigquery","--stdio"],
"env": {
"BIGQUERY_PROJECT": ""
}
}
}
}
```
1. Restart Claude desktop.
1. From the new chat screen, you should see a hammer (MCP) icon appear with the new MCP server available.
{{% /tab %}}
{{% tab header="Cline" lang="en" %}}
1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and tap the **MCP Servers** icon.
1. Tap Configure MCP Servers to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"bigquery": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","bigquery","--stdio"],
"env": {
"BIGQUERY_PROJECT": ""
}
}
}
}
```
1. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Cursor" lang="en" %}}
1. Create a `.cursor` directory in your project root if it doesn't exist.
1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"bigquery": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","bigquery","--stdio"],
"env": {
"BIGQUERY_PROJECT": ""
}
}
}
}
```
1. [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor Settings > MCP**. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and create a `.vscode` directory in your project root if it doesn't exist.
1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"bigquery": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","bigquery","--stdio"],
"env": {
"BIGQUERY_PROJECT": ""
}
}
}
}
```
{{% /tab %}}
{{% tab header="Windsurf" lang="en" %}}
1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the Cascade assistant.
1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"bigquery": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","bigquery","--stdio"],
"env": {
"BIGQUERY_PROJECT": ""
}
}
}
}
```
{{% /tab %}}
{{< /tabpane >}}
## Use Tools
Your AI tool is now connected to BigQuery using MCP. Try asking your AI assistant to list tables, create a table, or define and execute other SQL statements.
The following tools are available to the LLM:
1. **execute_sql**: execute SQL statement
1. **get_dataset_info**: get dataset metadata
1. **get_table_info**: get table metadata
1. **list_dataset_ids**: list datasets
1. **list_table_ids**: list tables
{{< notice note >}}
Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs will adapt to the tools available, so this shouldn't affect most users.
{{< /notice >}}

View File

@@ -5,9 +5,265 @@ weight: 2
description: >
Connect your IDE to Cloud SQL for SQL Server using Toolbox.
---
<html>
<head>
<link rel="canonical" href="https://cloud.google.com/sql/docs/sqlserver/pre-built-tools-with-mcp-toolbox"/>
<meta http-equiv="refresh" content="0;url=https://cloud.google.com/sql/docs/sqlserver/pre-built-tools-with-mcp-toolbox"/>
</head>
</html>
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol for connecting Large Language Models (LLMs) to data sources like Cloud SQL. This guide covers how to use [MCP Toolbox for Databases][toolbox] to expose your developer assistant tools to a Cloud SQL for SQL Server instance:
* [Cursor][cursor]
* [Windsurf][windsurf] (Codium)
* [Visual Studio Code ][vscode] (Copilot)
* [Cline][cline] (VS Code extension)
* [Claude desktop][claudedesktop]
* [Claude code][claudecode]
[toolbox]: https://github.com/googleapis/genai-toolbox
[cursor]: #configure-your-mcp-client
[windsurf]: #configure-your-mcp-client
[vscode]: #configure-your-mcp-client
[cline]: #configure-your-mcp-client
[claudedesktop]: #configure-your-mcp-client
[claudecode]: #configure-your-mcp-client
## Before you begin
1. In the Google Cloud console, on the [project selector page](https://console.cloud.google.com/projectselector2/home/dashboard), select or create a Google Cloud project.
1. [Make sure that billing is enabled for your Google Cloud project](https://cloud.google.com/billing/docs/how-to/verify-billing-enabled#confirm_billing_is_enabled_on_a_project).
## Set up the database
1. [Enable the Cloud SQL Admin API in the Google Cloud project](https://console.cloud.google.com/flows/enableapi?apiid=sqladmin&redirect=https://console.cloud.google.com).
1. [Create or select a Cloud SQL for SQL Server instance](https://cloud.google.com/sql/docs/sqlserver/create-instance). These instructions assume that your Cloud SQL instance has a [public IP address](https://cloud.google.com/sql/docs/sqlserver/configure-ip). By default, Cloud SQL assigns a public IP address to a new instance. Toolbox will connect securely using the [Cloud SQL connectors](https://cloud.google.com/sql/docs/sqlserver/language-connectors).
1. Configure the required roles and permissions to complete this task. You will need [Cloud SQL > Client](https://cloud.google.com/sql/docs/sqlserver/roles-and-permissions#proxy-roles-permissions) role (`roles/cloudsql.client`) or equivalent IAM permissions to connect to the instance.
1. Configured [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment) for your environment.
1. Create or reuse [a database user](https://cloud.google.com/sql/docs/sqlserver/create-manage-users) and have the username and password ready.
## Install MCP Toolbox
1. Download the latest version of Toolbox as a binary. Select the [correct binary](https://github.com/googleapis/genai-toolbox/releases) corresponding to your OS and CPU architecture. You are required to use Toolbox version V0.6.0+:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/windows/amd64/toolbox
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->
1. Make the binary executable:
```bash
chmod +x toolbox
```
1. Verify the installation:
```bash
./toolbox --version
```
## Configure your MCP Client
{{< tabpane text=true >}}
{{% tab header="Claude code" lang="en" %}}
1. Install [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
1. Create a `.mcp.json` file in your project root if it doesn't exist.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-sqlserver": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mssql","--stdio"],
"env": {
"CLOUD_SQL_MSSQL_PROJECT": "",
"CLOUD_SQL_MSSQL_REGION": "",
"CLOUD_SQL_MSSQL_INSTANCE": "",
"CLOUD_SQL_MSSQL_DATABASE": "",
"CLOUD_SQL_MSSQL_IP_ADDRESS": "",
"CLOUD_SQL_MSSQL_USER": "",
"CLOUD_SQL_MSSQL_PASSWORD": ""
}
}
}
```
1. Restart Claude code to apply the new configuration.
{{% /tab %}}
{{% tab header="Claude desktop" lang="en" %}}
1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
1. Under the Developer tab, tap Edit Config to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-sqlserver": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mssql","--stdio"],
"env": {
"CLOUD_SQL_MSSQL_PROJECT": "",
"CLOUD_SQL_MSSQL_REGION": "",
"CLOUD_SQL_MSSQL_INSTANCE": "",
"CLOUD_SQL_MSSQL_DATABASE": "",
"CLOUD_SQL_MSSQL_IP_ADDRESS": "",
"CLOUD_SQL_MSSQL_USER": "",
"CLOUD_SQL_MSSQL_PASSWORD": ""
}
}
}
}
```
1. Restart Claude desktop.
1. From the new chat screen, you should see a hammer (MCP) icon appear with the new MCP server available.
{{% /tab %}}
{{% tab header="Cline" lang="en" %}}
1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and tap the **MCP Servers** icon.
1. Tap Configure MCP Servers to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-sqlserver": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mssql","--stdio"],
"env": {
"CLOUD_SQL_MSSQL_PROJECT": "",
"CLOUD_SQL_MSSQL_REGION": "",
"CLOUD_SQL_MSSQL_INSTANCE": "",
"CLOUD_SQL_MSSQL_DATABASE": "",
"CLOUD_SQL_MSSQL_IP_ADDRESS": "",
"CLOUD_SQL_MSSQL_USER": "",
"CLOUD_SQL_MSSQL_PASSWORD": ""
}
}
}
}
```
1. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Cursor" lang="en" %}}
1. Create a `.cursor` directory in your project root if it doesn't exist.
1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-sqlserver": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mssql","--stdio"],
"env": {
"CLOUD_SQL_MSSQL_PROJECT": "",
"CLOUD_SQL_MSSQL_REGION": "",
"CLOUD_SQL_MSSQL_INSTANCE": "",
"CLOUD_SQL_MSSQL_DATABASE": "",
"CLOUD_SQL_MSSQL_IP_ADDRESS": "",
"CLOUD_SQL_MSSQL_USER": "",
"CLOUD_SQL_MSSQL_PASSWORD": ""
}
}
}
}
```
1. [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor Settings > MCP**. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and create a `.vscode` directory in your project root if it doesn't exist.
1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-sqlserver": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mssql","--stdio"],
"env": {
"CLOUD_SQL_MSSQL_PROJECT": "",
"CLOUD_SQL_MSSQL_REGION": "",
"CLOUD_SQL_MSSQL_INSTANCE": "",
"CLOUD_SQL_MSSQL_DATABASE": "",
"CLOUD_SQL_MSSQL_IP_ADDRESS": "",
"CLOUD_SQL_MSSQL_USER": "",
"CLOUD_SQL_MSSQL_PASSWORD": ""
}
}
}
}
```
{{% /tab %}}
{{% tab header="Windsurf" lang="en" %}}
1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the Cascade assistant.
1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-sqlserver": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mssql","--stdio"],
"env": {
"CLOUD_SQL_MSSQL_PROJECT": "",
"CLOUD_SQL_MSSQL_REGION": "",
"CLOUD_SQL_MSSQL_INSTANCE": "",
"CLOUD_SQL_MSSQL_DATABASE": "",
"CLOUD_SQL_MSSQL_IP_ADDRESS": "",
"CLOUD_SQL_MSSQL_USER": "",
"CLOUD_SQL_MSSQL_PASSWORD": ""
}
}
}
}
```
{{% /tab %}}
{{< /tabpane >}}
## Use Tools
Your AI tool is now connected to Cloud SQL for Sql Server using MCP. Try asking your AI assistant to list tables, create a table, or define and execute other SQL statements.
The following tools are available to the LLM:
1. **list_tables**: lists tables and descriptions
1. **execute_sql**: execute any SQL statement
{{< notice note >}}
Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs will adapt to the tools available, so this shouldn't affect most users.
{{< /notice >}}

View File

@@ -5,9 +5,259 @@ weight: 2
description: >
Connect your IDE to Cloud SQL for MySQL using Toolbox.
---
<html>
<head>
<link rel="canonical" href="https://cloud.google.com/sql/docs/mysql/pre-built-tools-with-mcp-toolbox"/>
<meta http-equiv="refresh" content="0;url=https://cloud.google.com/sql/docs/mysql/pre-built-tools-with-mcp-toolbox"/>
</head>
</html>
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol for connecting Large Language Models (LLMs) to data sources like Cloud SQL. This guide covers how to use [MCP Toolbox for Databases][toolbox] to expose your developer assistant tools to a Cloud SQL for MySQL instance:
* [Cursor][cursor]
* [Windsurf][windsurf] (Codium)
* [Visual Studio Code ][vscode] (Copilot)
* [Cline][cline] (VS Code extension)
* [Claude desktop][claudedesktop]
* [Claude code][claudecode]
[toolbox]: https://github.com/googleapis/genai-toolbox
[cursor]: #configure-your-mcp-client
[windsurf]: #configure-your-mcp-client
[vscode]: #configure-your-mcp-client
[cline]: #configure-your-mcp-client
[claudedesktop]: #configure-your-mcp-client
[claudecode]: #configure-your-mcp-client
## Before you begin
1. In the Google Cloud console, on the [project selector page](https://console.cloud.google.com/projectselector2/home/dashboard), select or create a Google Cloud project.
1. [Make sure that billing is enabled for your Google Cloud project](https://cloud.google.com/billing/docs/how-to/verify-billing-enabled#confirm_billing_is_enabled_on_a_project).
## Set up the database
1. [Enable the Cloud SQL Admin API in the Google Cloud project](https://console.cloud.google.com/flows/enableapi?apiid=sqladmin&redirect=https://console.cloud.google.com).
1. [Create a Cloud SQL for MySQL instance](https://cloud.google.com/sql/docs/mysql/create-instance). These instructions assume that your Cloud SQL instance has a [public IP address](https://cloud.google.com/sql/docs/mysql/configure-ip). By default, Cloud SQL assigns a public IP address to a new instance. Toolbox will connect securely using the [Cloud SQL connectors](https://cloud.google.com/sql/docs/mysql/language-connectors).
1. Configure the required roles and permissions to complete this task. You will need [Cloud SQL > Client](https://cloud.google.com/sql/docs/mysql/roles-and-permissions#proxy-roles-permissions) role (`roles/cloudsql.client`) or equivalent IAM permissions to connect to the instance.
1. Configured [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment) for your environment.
1. Create or reuse [a database user](https://cloud.google.com/sql/docs/mysql/create-manage-users) and have the username and password ready.
## Install MCP Toolbox
1. Download the latest version of Toolbox as a binary. Select the [correct binary](https://github.com/googleapis/genai-toolbox/releases) corresponding to your OS and CPU architecture. You are required to use Toolbox version V0.6.0+:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/windows/amd64/toolbox
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->
1. Make the binary executable:
```bash
chmod +x toolbox
```
1. Verify the installation:
```bash
./toolbox --version
```
## Configure your MCP Client
{{< tabpane text=true >}}
{{% tab header="Claude code" lang="en" %}}
1. Install [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
1. Create a `.mcp.json` file in your project root if it doesn't exist.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-mysql": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mysql","--stdio"],
"env": {
"CLOUD_SQL_MYSQL_PROJECT": "",
"CLOUD_SQL_MYSQL_REGION": "",
"CLOUD_SQL_MYSQL_INSTANCE": "",
"CLOUD_SQL_MYSQL_DATABASE": "",
"CLOUD_SQL_MYSQL_USER": "",
"CLOUD_SQL_MYSQL_PASSWORD": ""
}
}
}
}
```
1. Restart Claude code to apply the new configuration.
{{% /tab %}}
{{% tab header="Claude desktop" lang="en" %}}
1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
1. Under the Developer tab, tap Edit Config to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-mysql": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mysql","--stdio"],
"env": {
"CLOUD_SQL_MYSQL_PROJECT": "",
"CLOUD_SQL_MYSQL_REGION": "",
"CLOUD_SQL_MYSQL_INSTANCE": "",
"CLOUD_SQL_MYSQL_DATABASE": "",
"CLOUD_SQL_MYSQL_USER": "",
"CLOUD_SQL_MYSQL_PASSWORD": ""
}
}
}
}
```
1. Restart Claude desktop.
1. From the new chat screen, you should see a hammer (MCP) icon appear with the new MCP server available.
{{% /tab %}}
{{% tab header="Cline" lang="en" %}}
1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and tap the **MCP Servers** icon.
1. Tap Configure MCP Servers to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-mysql": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mysql","--stdio"],
"env": {
"CLOUD_SQL_MYSQL_PROJECT": "",
"CLOUD_SQL_MYSQL_REGION": "",
"CLOUD_SQL_MYSQL_INSTANCE": "",
"CLOUD_SQL_MYSQL_DATABASE": "",
"CLOUD_SQL_MYSQL_USER": "",
"CLOUD_SQL_MYSQL_PASSWORD": ""
}
}
}
}
```
1. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Cursor" lang="en" %}}
1. Create a `.cursor` directory in your project root if it doesn't exist.
1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-mysql": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mysql","--stdio"],
"env": {
"CLOUD_SQL_MYSQL_PROJECT": "",
"CLOUD_SQL_MYSQL_REGION": "",
"CLOUD_SQL_MYSQL_INSTANCE": "",
"CLOUD_SQL_MYSQL_DATABASE": "",
"CLOUD_SQL_MYSQL_USER": "",
"CLOUD_SQL_MYSQL_PASSWORD": ""
}
}
}
```
1. [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor Settings > MCP**. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and create a `.vscode` directory in your project root if it doesn't exist.
1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-mysql": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mysql","--stdio"],
"env": {
"CLOUD_SQL_MYSQL_PROJECT": "",
"CLOUD_SQL_MYSQL_REGION": "",
"CLOUD_SQL_MYSQL_INSTANCE": "",
"CLOUD_SQL_MYSQL_DATABASE": "",
"CLOUD_SQL_MYSQL_USER": "",
"CLOUD_SQL_MYSQL_PASSWORD": ""
}
}
}
}
```
{{% /tab %}}
{{% tab header="Windsurf" lang="en" %}}
1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the Cascade assistant.
1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-mysql": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-mysql","--stdio"],
"env": {
"CLOUD_SQL_MYSQL_PROJECT": "",
"CLOUD_SQL_MYSQL_REGION": "",
"CLOUD_SQL_MYSQL_INSTANCE": "",
"CLOUD_SQL_MYSQL_DATABASE": "",
"CLOUD_SQL_MYSQL_USER": "",
"CLOUD_SQL_MYSQL_PASSWORD": ""
}
}
}
}
```
{{% /tab %}}
{{< /tabpane >}}
## Use Tools
Your AI tool is now connected to Cloud SQL for MySQL using MCP. Try asking your AI assistant to list tables, create a table, or define and execute other SQL statements.
The following tools are available to the LLM:
1. **list_tables**: lists tables and descriptions
1. **execute_sql**: execute any SQL statement
{{< notice note >}}
Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs will adapt to the tools available, so this shouldn't affect most users.
{{< /notice >}}

View File

@@ -5,9 +5,260 @@ weight: 2
description: >
Connect your IDE to Cloud SQL for Postgres using Toolbox.
---
<html>
<head>
<link rel="canonical" href="https://cloud.google.com/sql/docs/postgres/pre-built-tools-with-mcp-toolbox"/>
<meta http-equiv="refresh" content="0;url=https://cloud.google.com/sql/docs/postgres/pre-built-tools-with-mcp-toolbox"/>
</head>
</html>
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol for connecting Large Language Models (LLMs) to data sources like Cloud SQL. This guide covers how to use [MCP Toolbox for Databases][toolbox] to expose your developer assistant tools to a Cloud SQL for Postgres instance:
* [Cursor][cursor]
* [Windsurf][windsurf] (Codium)
* [Visual Studio Code ][vscode] (Copilot)
* [Cline][cline] (VS Code extension)
* [Claude desktop][claudedesktop]
* [Claude code][claudecode]
[toolbox]: https://github.com/googleapis/genai-toolbox
[cursor]: #configure-your-mcp-client
[windsurf]: #configure-your-mcp-client
[vscode]: #configure-your-mcp-client
[cline]: #configure-your-mcp-client
[claudedesktop]: #configure-your-mcp-client
[claudecode]: #configure-your-mcp-client
## Before you begin
1. In the Google Cloud console, on the [project selector page](https://console.cloud.google.com/projectselector2/home/dashboard), select or create a Google Cloud project.
1. [Make sure that billing is enabled for your Google Cloud project](https://cloud.google.com/billing/docs/how-to/verify-billing-enabled#confirm_billing_is_enabled_on_a_project).
## Set up the database
1. [Enable the Cloud SQL Admin API in the Google Cloud project](https://console.cloud.google.com/flows/enableapi?apiid=sqladmin&redirect=https://console.cloud.google.com).
1. [Create or select a Cloud SQL for PostgreSQL instance](https://cloud.google.com/sql/docs/postgres/create-instance). These instructions assume that your Cloud SQL instance has a [public IP address](https://cloud.google.com/sql/docs/postgres/configure-ip). By default, Cloud SQL assigns a public IP address to a new instance. Toolbox will connect securely using the [Cloud SQL connectors](https://cloud.google.com/sql/docs/postgres/language-connectors).
1. Configure the required roles and permissions to complete this task. You will need [Cloud SQL > Client](https://cloud.google.com/sql/docs/postgres/roles-and-permissions#proxy-roles-permissions) role (`roles/cloudsql.client`) or equivalent IAM permissions to connect to the instance.
1. Configured [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment) for your environment.
1. Create or reuse [a database user](https://cloud.google.com/sql/docs/postgres/create-manage-users) and have the username and password ready.
## Install MCP Toolbox
1. Download the latest version of Toolbox as a binary. Select the [correct binary](https://github.com/googleapis/genai-toolbox/releases) corresponding to your OS and CPU architecture. You are required to use Toolbox version V0.6.0+:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/windows/amd64/toolbox
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->
1. Make the binary executable:
```bash
chmod +x toolbox
```
1. Verify the installation:
```bash
./toolbox --version
```
## Configure your MCP Client
{{< tabpane text=true >}}
{{% tab header="Claude code" lang="en" %}}
1. Install [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
1. Create a `.mcp.json` file in your project root if it doesn't exist.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-postgres": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-postgres","--stdio"],
"env": {
"CLOUD_SQL_POSTGRES_PROJECT": "",
"CLOUD_SQL_POSTGRES_REGION": "",
"CLOUD_SQL_POSTGRES_INSTANCE": "",
"CLOUD_SQL_POSTGRES_DATABASE": "",
"CLOUD_SQL_POSTGRES_USER": "",
"CLOUD_SQL_POSTGRES_PASSWORD": ""
}
}
}
}
```
1. Restart Claude code to apply the new configuration.
{{% /tab %}}
{{% tab header="Claude desktop" lang="en" %}}
1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
1. Under the Developer tab, tap Edit Config to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-postgres": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-postgres","--stdio"],
"env": {
"CLOUD_SQL_POSTGRES_PROJECT": "",
"CLOUD_SQL_POSTGRES_REGION": "",
"CLOUD_SQL_POSTGRES_INSTANCE": "",
"CLOUD_SQL_POSTGRES_DATABASE": "",
"CLOUD_SQL_POSTGRES_USER": "",
"CLOUD_SQL_POSTGRES_PASSWORD": ""
}
}
}
}
```
1. Restart Claude desktop.
1. From the new chat screen, you should see a hammer (MCP) icon appear with the new MCP server available.
{{% /tab %}}
{{% tab header="Cline" lang="en" %}}
1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and tap the **MCP Servers** icon.
1. Tap Configure MCP Servers to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-postgres": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-postgres","--stdio"],
"env": {
"CLOUD_SQL_POSTGRES_PROJECT": "",
"CLOUD_SQL_POSTGRES_REGION": "",
"CLOUD_SQL_POSTGRES_INSTANCE": "",
"CLOUD_SQL_POSTGRES_DATABASE": "",
"CLOUD_SQL_POSTGRES_USER": "",
"CLOUD_SQL_POSTGRES_PASSWORD": ""
}
}
}
}
```
1. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Cursor" lang="en" %}}
1. Create a `.cursor` directory in your project root if it doesn't exist.
1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-postgres": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-postgres","--stdio"],
"env": {
"CLOUD_SQL_POSTGRES_PROJECT": "",
"CLOUD_SQL_POSTGRES_REGION": "",
"CLOUD_SQL_POSTGRES_INSTANCE": "",
"CLOUD_SQL_POSTGRES_DATABASE": "",
"CLOUD_SQL_POSTGRES_USER": "",
"CLOUD_SQL_POSTGRES_PASSWORD": ""
}
}
}
}
```
1. [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor Settings > MCP**. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and create a `.vscode` directory in your project root if it doesn't exist.
1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-postgres": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-postgres","--stdio"],
"env": {
"CLOUD_SQL_POSTGRES_PROJECT": "",
"CLOUD_SQL_POSTGRES_REGION": "",
"CLOUD_SQL_POSTGRES_INSTANCE": "",
"CLOUD_SQL_POSTGRES_DATABASE": "",
"CLOUD_SQL_POSTGRES_USER": "",
"CLOUD_SQL_POSTGRES_PASSWORD": ""
}
}
}
}
```
{{% /tab %}}
{{% tab header="Windsurf" lang="en" %}}
1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the Cascade assistant.
1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
```json
{
"mcpServers": {
"cloud-sql-postgres": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","cloud-sql-postgres","--stdio"],
"env": {
"CLOUD_SQL_POSTGRES_PROJECT": "",
"CLOUD_SQL_POSTGRES_REGION": "",
"CLOUD_SQL_POSTGRES_INSTANCE": "",
"CLOUD_SQL_POSTGRES_DATABASE": "",
"CLOUD_SQL_POSTGRES_USER": "",
"CLOUD_SQL_POSTGRES_PASSWORD": ""
}
}
}
}
```
{{% /tab %}}
{{< /tabpane >}}
## Use Tools
Your AI tool is now connected to Cloud SQL for PostgreSQL using MCP. Try asking your AI assistant to list tables, create a table, or define and execute other SQL statements.
The following tools are available to the LLM:
1. **list_tables**: lists tables and descriptions
1. **execute_sql**: execute any SQL statement
{{< notice note >}}
Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs will adapt to the tools available, so this shouldn't affect most users.
{{< /notice >}}

View File

@@ -5,9 +5,354 @@ weight: 2
description: >
Connect your IDE to Spanner using Toolbox.
---
<html>
<head>
<link rel="canonical" href="https://cloud.google.com/spanner/docs/pre-built-tools-with-mcp-toolbox"/>
<meta http-equiv="refresh" content="0;url=https://cloud.google.com/spanner/docs/pre-built-tools-with-mcp-toolbox"/>
</head>
</html>
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol for connecting Large Language Models (LLMs) to data sources like Spanner. This guide covers how to use [MCP Toolbox for Databases][toolbox] to expose your developer assistant tools to a Spanner instance:
* [Cursor][cursor]
* [Windsurf][windsurf] (Codium)
* [Visual Studio Code ][vscode] (Copilot)
* [Cline][cline] (VS Code extension)
* [Claude desktop][claudedesktop]
* [Claude code][claudecode]
[toolbox]: https://github.com/googleapis/genai-toolbox
[cursor]: #configure-your-mcp-client
[windsurf]: #configure-your-mcp-client
[vscode]: #configure-your-mcp-client
[cline]: #configure-your-mcp-client
[claudedesktop]: #configure-your-mcp-client
[claudecode]: #configure-your-mcp-client
## Before you begin
1. In the Google Cloud console, on the [project selector page](https://console.cloud.google.com/projectselector2/home/dashboard), select or create a Google Cloud project.
1. [Make sure that billing is enabled for your Google Cloud project](https://cloud.google.com/billing/docs/how-to/verify-billing-enabled#confirm_billing_is_enabled_on_a_project).
## Set up the database
1. [Enable the Spanner API in the Google Cloud project](https://console.cloud.google.com/flows/enableapi?apiid=spanner.googleapis.com&redirect=https://console.cloud.google.com).
1. [Create or select a Spanner instance and database](https://cloud.google.com/spanner/docs/create-query-database-console).
1. Configure the required roles and permissions to complete this task. You will need [Cloud Spanner Database User](https://cloud.google.com/spanner/docs/iam#roles) role (`roles/spanner.databaseUser`) or equivalent IAM permissions to connect to the instance.
1. Configured [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment) for your environment.
## Install MCP Toolbox
1. Download the latest version of Toolbox as a binary. Select the [correct binary](https://github.com/googleapis/genai-toolbox/releases) corresponding to your OS and CPU architecture. You are required to use Toolbox version V0.6.0+:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.7.0/windows/amd64/toolbox
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->
1. Make the binary executable:
```bash
chmod +x toolbox
```
1. Verify the installation:
```bash
./toolbox --version
```
## Configure your MCP Client
{{< tabpane text=true >}}
{{% tab header="Claude code" lang="en" %}}
1. Install [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
1. Create a `.mcp.json` file in your project root if it doesn't exist.
1. Add the following configuration, replace the environment variables with your values, and save:
Spanner with `googlesql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
Spanner with `postgresql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner-postgres","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": "" }
}
}
}
```
1. Restart Claude code to apply the new configuration.
{{% /tab %}}
{{% tab header="Claude desktop" lang="en" %}}
1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
1. Under the Developer tab, tap Edit Config to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
Spanner with `googlesql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
Spanner with `postgresql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner-postgres","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
1. Restart Claude desktop.
1. From the new chat screen, you should see a hammer (MCP) icon appear with the new MCP server available.
{{% /tab %}}
{{% tab header="Cline" lang="en" %}}
1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and tap the **MCP Servers** icon.
1. Tap Configure MCP Servers to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
Spanner with `googlesql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
Spanner with `postgresql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner-postgres","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
1. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Cursor" lang="en" %}}
1. Create a `.cursor` directory in your project root if it doesn't exist.
1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
Spanner with `googlesql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
Spanner with `postgresql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner-postgres","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
1. [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor Settings > MCP**. You should see a green active status after the server is successfully connected.
{{% /tab %}}
{{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and create a `.vscode` directory in your project root if it doesn't exist.
1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
1. Add the following configuration, replace the environment variables with your values, and save:
Spanner with `googlesql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
Spanner with `postgresql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner-postgres","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
{{% /tab %}}
{{% tab header="Windsurf" lang="en" %}}
1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the Cascade assistant.
1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
1. Add the following configuration, replace the environment variables with your values, and save:
Spanner with `googlesql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
Spanner with `postgresql` dialect
```json
{
"mcpServers": {
"spanner": {
"command": "./PATH/TO/toolbox",
"args": ["--prebuilt","spanner-postgres","--stdio"],
"env": {
"SPANNER_PROJECT": "",
"SPANNER_INSTANCE": "",
"SPANNER_DATABASE": ""
}
}
}
}
```
{{% /tab %}}
{{< /tabpane >}}
## Use Tools
Your AI tool is now connected to Spanner using MCP. Try asking your AI assistant to list tables, create a table, or define and execute other SQL statements.
The following tools are available to the LLM:
1. **list_tables**: lists tables and descriptions
1. **execute_sql**: execute DML SQL statement
1. **execute_sql_dql**: execute DQL SQL statement
{{< notice note >}}
Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs will adapt to the tools available, so this shouldn't affect most users.
{{< /notice >}}

View File

@@ -27,7 +27,6 @@ sources:
queryParams:
param1: value1
param2: value2
# disableSslVerification: false
```
{{< notice tip >}}
@@ -37,13 +36,12 @@ instead of hardcoding your secrets into the configuration file.
## Reference
| **field** | **type** | **required** | **description** |
|------------------------|:-----------------:|:------------:|------------------------------------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "http". |
| baseUrl | string | true | The base URL for the HTTP requests (e.g., `https://api.example.com`). |
| timeout | string | false | The timeout for HTTP requests (e.g., "5s", "1m", refer to [ParseDuration][parse-duration-doc] for more examples). Defaults to 30s. |
| headers | map[string]string | false | Default headers to include in the HTTP requests. |
| queryParams | map[string]string | false | Default query parameters to include in the HTTP requests. |
| disableSslVerification | bool | false | Disable SSL certificate verification. This should only be used for local development. Defaults to `false`. |
| **field** | **type** | **required** | **description** |
|-------------|:-----------------:|:------------:|-----------------------------------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "http". |
| baseUrl | string | true | The base URL for the HTTP requests (e.g., `https://api.example.com`). |
| timeout | string | false | The timeout for HTTP requests (e.g., "5s", "1m", refer to [ParseDuration][parse-duration-doc] for more examples). Defaults to 30s. |
| headers | map[string]string | false | Default headers to include in the HTTP requests. |
| queryParams | map[string]string | false | Default query parameters to include in the HTTP requests. |
[parse-duration-doc]: https://pkg.go.dev/time#ParseDuration

View File

@@ -79,12 +79,11 @@ the parameter.
description: Airline unique 2 letter identifier
```
| **field** | **type** | **required** | **description** |
|-------------|:---------------:|:------------:|-----------------------------------------------------------------------------|
| name | string | true | Name of the parameter. |
| type | string | true | Must be one of "string", "integer", "float", "boolean" "array" |
| default | parameter type | false | Default value of the parameter. If provided, the parameter is not required. |
| description | string | true | Natural language description of the parameter to describe it to the agent. |
| **field** | **type** | **required** | **description** |
|-------------|:--------:|:------------:|----------------------------------------------------------------------------|
| name | string | true | Name of the parameter. |
| type | string | true | Must be one of "string", "integer", "float", "boolean" "array" |
| description | string | true | Natural language description of the parameter to describe it to the agent. |
### Array Parameters
@@ -103,17 +102,12 @@ in the list using the items field:
description: Name of the airline.
```
| **field** | **type** | **required** | **description** |
|-------------|:----------------:|:------------:|-----------------------------------------------------------------------------|
| name | string | true | Name of the parameter. |
| type | string | true | Must be "array" |
| default | parameter type | false | Default value of the parameter. If provided, the parameter is not required. |
| description | string | true | Natural language description of the parameter to describe it to the agent. |
| items | parameter object | true | Specify a Parameter object for the type of the values in the array. |
{{< notice note >}}
Items in array should not have a default value. If provided, it will be ignored.
{{< /notice >}}
| **field** | **type** | **required** | **description** |
|-------------|:----------------:|:------------:|----------------------------------------------------------------------------|
| name | string | true | Name of the parameter. |
| type | string | true | Must be "array" |
| description | string | true | Natural language description of the parameter to describe it to the agent. |
| items | parameter object | true | Specify a Parameter object for the type of the values in the array. |
### Authenticated Parameters

View File

@@ -20,16 +20,12 @@ parameters can be inserted into the query. BigQuery supports both named paramete
(e.g., `@name`) and positional parameters (`?`), but they cannot be mixed in the
same query.
> **Note:** This tool uses [parameterized queries](https://cloud.google.com/bigquery/docs/parameterized-queries) to prevent SQL injections. Query parameters can be used as substitutes for arbitrary expressions. Parameters cannot be used as substitutes for identifiers, column names, table names, or other parts of the query.
[bigquery-googlesql]: https://cloud.google.com/bigquery/docs/reference/standard-sql/
## Example
> **Note:** This tool uses [parameterized
> queries](https://cloud.google.com/bigquery/docs/parameterized-queries) to
> prevent SQL injections. Query parameters can be used as substitutes for
> arbitrary expressions. Parameters cannot be used as substitutes for
> identifiers, column names, table names, or other parts of the query.
```yaml
tools:
# Example: Querying a user table in BigQuery
@@ -63,40 +59,12 @@ tools:
description: Email address of the user
```
### Example with Template Parameters
> **Note:** This tool allows direct modifications to the SQL statement,
> including identifiers, column names, and table names. **This makes it more
> vulnerable to SQL injections**. Using basic parameters only (see above) is
> recommended for performance and safety reasons. For more details, please check
> [templateParameters](_index#template-parameters).
```yaml
tools:
list_table:
kind: bigquery-sql
source: my-bigquery-source
statement: |
SELECT * FROM {{.tableName}};
description: |
Use this tool to list all information from a specific table.
Example:
{{
"tableName": "flights",
}}
templateParameters:
- name: tableName
type: string
description: Table to select from
```
## Reference
| **field** | **type** | **required** | **description** |
|--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "bigquery-sql". |
| source | string | true | Name of the source the GoogleSQL should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | The GoogleSQL statement to execute. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |
| templateParameters | [templateParameters](_index#template-parameters) | false | List of [templateParameters](_index#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
| **field** | **type** | **required** | **description** |
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "bigquery-sql". |
| source | string | true | Name of the source the GoogleSQL should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | The GoogleSQL statement to execute. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |

View File

@@ -21,15 +21,15 @@ dialect, the specified SQL statement is executed as a [data manipulation
language (DML)][bigtable-googlesql] statements, and specified parameters will
inserted according to their name: e.g. `@name`.
[bigtable-googlesql]: https://cloud.google.com/bigtable/docs/googlesql-overview
## Example
> **Note:** This tool uses parameterized queries to prevent SQL injections.
> Query parameters can be used as substitutes for arbitrary expressions.
> Parameters cannot be used as substitutes for identifiers, column names, table
> names, or other parts of the query.
[bigtable-googlesql]: https://cloud.google.com/bigtable/docs/googlesql-overview
## Example
```yaml
tools:
search_user_by_id_or_name:
@@ -62,43 +62,15 @@ tools:
description: Name of the user
```
### Example with Template Parameters
> **Note:** This tool allows direct modifications to the SQL statement,
> including identifiers, column names, and table names. **This makes it more
> vulnerable to SQL injections**. Using basic parameters only (see above) is
> recommended for performance and safety reasons. For more details, please check
> [templateParameters](_index#template-parameters).
```yaml
tools:
list_table:
kind: bigtable-sql
source: my-bigtable-instance
statement: |
SELECT * FROM {{.tableName}};
description: |
Use this tool to list all information from a specific table.
Example:
{{
"tableName": "flights",
}}
templateParameters:
- name: tableName
type: string
description: Table to select from
```
## Reference
| **field** | **type** | **required** | **description** |
|--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "bigtable-sql". |
| source | string | true | Name of the source the SQL should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | SQL statement to execute on. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |
| templateParameters | [templateParameters](_index#template-parameters) | false | List of [templateParameters](_index#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
| **field** | **type** | **required** | **description** |
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "bigtable-sql". |
| source | string | true | Name of the source the SQL should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | SQL statement to execute on. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |
## Tips

View File

@@ -17,13 +17,13 @@ database. It's compatible with any of the following sources:
The specified SQL statement is executed as a parameterized statement, and specified
parameters will be used according to their name: e.g. `$id`.
## Example
> **Note:** This tool uses parameterized queries to prevent SQL injections.
> Query parameters can be used as substitutes for arbitrary expressions.
> Parameters cannot be used as substitutes for identifiers, column names, table
> names, or other parts of the query.
## Example
```yaml
tools:
search_products_by_category:
@@ -58,41 +58,13 @@ tools:
description: Maximum price (positive integer)
```
### Example with Template Parameters
> **Note:** This tool allows direct modifications to the SQL statement,
> including identifiers, column names, and table names. **This makes it more
> vulnerable to SQL injections**. Using basic parameters only (see above) is
> recommended for performance and safety reasons. For more details, please check
> [templateParameters](_index#template-parameters).
```yaml
tools:
list_table:
kind: couchbase-sql
source: my-couchbase-instance
statement: |
SELECT * FROM {{.tableName}};
description: |
Use this tool to list all information from a specific table.
Example:
{{
"tableName": "flights",
}}
templateParameters:
- name: tableName
type: string
description: Table to select from
```
## Reference
| **field** | **type** | **required** | **description** |
|--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "couchbase-sql". |
| source | string | true | Name of the source the SQL query should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | SQL statement to execute |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be used with the SQL statement. |
| templateParameters | [templateParameters](_index#template-parameters) | false | List of [templateParameters](_index#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
| authRequired | array[string] | false | List of auth services that are required to use this tool. |
| **field** | **type** | **required** | **description** |
|-------------|:------------------------------------------:|:------------:|------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "couchbase-sql". |
| source | string | true | Name of the source the SQL query should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | SQL statement to execute |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be used with the SQL statement. |
| authRequired| array[string] | false | List of auth services that are required to use this tool. |

View File

@@ -16,12 +16,7 @@ Toolbox allows you to configure the request URL, method, headers, query paramete
### URL
An HTTP request URL identifies the target the client wants to access.
Toolbox composes the request URL from 3 places:
1. The HTTP Source's `baseUrl`.
2. The HTTP Tool's `path` field.
3. The HTTP Tool's `pathParams` for dynamic path composed during Tool invocation.
Toolbox composes the request URL from the HTTP Source's `baseUrl` and the HTTP Tool's `path`.
For example, the following config allows you to reach different paths of the same server using multiple Tools:
```yaml
@@ -44,17 +39,6 @@ tools:
method: GET
path: /search
description: Tool to search information from the example API
my-dynamic-path-tool:
kind: http
source: my-http-source
method: GET
path: /{{.myPathParam}}/search
description: Tool to reach endpoint based on the input to `myPathParam`
pathParams:
- name: myPathParam
type: string
description: The dynamic path parameter
```

View File

@@ -23,15 +23,15 @@ Server and expects parameters in the SQL query to be in the form of either
db.QueryContext(ctx, `select * from t where ID = @ID and Name = @p2;`, sql.Named("ID", 6), "Bob")
```
[prepare-statement]: https://learn.microsoft.com/sql/relational-databases/system-stored-procedures/sp-prepare-transact-sql?view=sql-server-ver16
## Example
> **Note:** This tool uses parameterized queries to prevent SQL injections.
> Query parameters can be used as substitutes for arbitrary expressions.
> Parameters cannot be used as substitutes for identifiers, column names, table
> names, or other parts of the query.
[prepare-statement]: https://learn.microsoft.com/sql/relational-databases/system-stored-procedures/sp-prepare-transact-sql?view=sql-server-ver16
## Example
```yaml
tools:
search_flights_by_number:
@@ -70,40 +70,12 @@ tools:
description: 1 to 4 digit number
```
### Example with Template Parameters
> **Note:** This tool allows direct modifications to the SQL statement,
> including identifiers, column names, and table names. **This makes it more
> vulnerable to SQL injections**. Using basic parameters only (see above) is
> recommended for performance and safety reasons. For more details, please check
> [templateParameters](_index#template-parameters).
```yaml
tools:
list_table:
kind: mssql-sql
source: my-instance
statement: |
SELECT * FROM {{.tableName}};
description: |
Use this tool to list all information from a specific table.
Example:
{{
"tableName": "flights",
}}
templateParameters:
- name: tableName
type: string
description: Table to select from
```
## Reference
| **field** | **type** | **required** | **description** |
|--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "mssql-sql". |
| source | string | true | Name of the source the T-SQL statement should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | SQL statement to execute. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |
| templateParameters | [templateParameters](_index#template-parameters) | false | List of [templateParameters](_index#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
| **field** | **type** | **required** | **description** |
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "mssql-sql". |
| source | string | true | Name of the source the T-SQL statement should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | SQL statement to execute. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |

View File

@@ -18,15 +18,15 @@ database. It's compatible with any of the following sources:
The specified SQL statement is executed as a [prepared statement][mysql-prepare],
and expects parameters in the SQL query to be in the form of placeholders `?`.
[mysql-prepare]: https://dev.mysql.com/doc/refman/8.4/en/sql-prepared-statements.html
## Example
> **Note:** This tool uses parameterized queries to prevent SQL injections.
> Query parameters can be used as substitutes for arbitrary expressions.
> Parameters cannot be used as substitutes for identifiers, column names, table
> names, or other parts of the query.
[mysql-prepare]: https://dev.mysql.com/doc/refman/8.4/en/sql-prepared-statements.html
## Example
```yaml
tools:
search_flights_by_number:
@@ -65,40 +65,12 @@ tools:
description: 1 to 4 digit number
```
### Example with Template Parameters
> **Note:** This tool allows direct modifications to the SQL statement,
> including identifiers, column names, and table names. **This makes it more
> vulnerable to SQL injections**. Using basic parameters only (see above) is
> recommended for performance and safety reasons. For more details, please check
> [templateParameters](_index#template-parameters).
```yaml
tools:
list_table:
kind: mysql-sql
source: my-mysql-instance
statement: |
SELECT * FROM {{.tableName}};
description: |
Use this tool to list all information from a specific table.
Example:
{{
"tableName": "flights",
}}
templateParameters:
- name: tableName
type: string
description: Table to select from
```
## Reference
| **field** | **type** | **required** | **description** |
|--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "mysql-sql". |
| source | string | true | Name of the source the SQL should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | SQL statement to execute on. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |
| templateParameters | [templateParameters](_index#template-parameters) | false | List of [templateParameters](_index#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
| **field** | **type** | **required** | **description** |
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "mysql-sql". |
| source | string | true | Name of the source the SQL should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | SQL statement to execute on. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |

View File

@@ -22,8 +22,6 @@ will be the first parameter specified, `$@` will be the second parameter, and so
on. If template parameters are included, they will be resolved before execution
of the prepared statement.
[pg-prepare]: https://www.postgresql.org/docs/current/sql-prepare.html
## Example
> **Note:** This tool uses parameterized queries to prevent SQL injections.
@@ -31,6 +29,8 @@ of the prepared statement.
> Parameters cannot be used as substitutes for identifiers, column names, table
> names, or other parts of the query.
[pg-prepare]: https://www.postgresql.org/docs/current/sql-prepare.html
```yaml
tools:
search_flights_by_number:
@@ -71,11 +71,10 @@ tools:
### Example with Template Parameters
> **Note:** This tool allows direct modifications to the SQL statement,
> including identifiers, column names, and table names. **This makes it more
> vulnerable to SQL injections**. Using basic parameters only (see above) is
> recommended for performance and safety reasons. For more details, please check
> [templateParameters](_index#template-parameters).
> **Note:** This tool allows direct modifications to the SQL statement, including identifiers, column names,
> and table names. **This makes it more vulnerable to SQL injections**. Using basic parameters
> only (see above) is recommended for performance and safety reasons. For more details, please
> check [templateParameters](_index#template-parameters).
```yaml
tools:

View File

@@ -35,15 +35,15 @@ statement][pg-prepare], and specified parameters will inserted according to
their position: e.g. `$1` will be the first parameter specified, `$@` will be
the second parameter, and so on.
[pg-prepare]: https://www.postgresql.org/docs/current/sql-prepare.html
## Example
> **Note:** This tool uses parameterized queries to prevent SQL injections.
> Query parameters can be used as substitutes for arbitrary expressions.
> Parameters cannot be used as substitutes for identifiers, column names, table
> names, or other parts of the query.
[pg-prepare]: https://www.postgresql.org/docs/current/sql-prepare.html
## Example
{{< tabpane persist="header" >}}
{{< tab header="GoogleSQL" lang="yaml" >}}
@@ -125,41 +125,13 @@ tools:
{{< /tab >}}
{{< /tabpane >}}
### Example with Template Parameters
> **Note:** This tool allows direct modifications to the SQL statement,
> including identifiers, column names, and table names. **This makes it more
> vulnerable to SQL injections**. Using basic parameters only (see above) is
> recommended for performance and safety reasons. For more details, please check
> [templateParameters](_index#template-parameters).
```yaml
tools:
list_table:
kind: spanner
source: my-spanner-instance
statement: |
SELECT * FROM {{.tableName}};
description: |
Use this tool to list all information from a specific table.
Example:
{{
"tableName": "flights",
}}
templateParameters:
- name: tableName
type: string
description: Table to select from
```
## Reference
| **field** | **type** | **required** | **description** |
|--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "spanner-sql". |
| source | string | true | Name of the source the SQL should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | SQL statement to execute on. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |
| readOnly | bool | false | When set to `true`, the `statement` is run as a read-only transaction. Default: `false`. |
| templateParameters | [templateParameters](_index#template-parameters) | false | List of [templateParameters](_index#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
| **field** | **type** | **required** | **description** |
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "spanner-sql". |
| source | string | true | Name of the source the SQL should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | SQL statement to execute on. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |
| readOnly | bool | false | When set to `true`, the `statement` is run as a read-only transaction. Default: `false`. |

View File

@@ -20,13 +20,13 @@ The statement field supports any valid SQLite SQL statement, including `SELECT`,
`INSERT`, `UPDATE`, `DELETE`, `CREATE/ALTER/DROP` table statements, and other
DDL statements.
### Example
> **Note:** This tool uses parameterized queries to prevent SQL injections.
> Query parameters can be used as substitutes for arbitrary expressions.
> Parameters cannot be used as substitutes for identifiers, column names, table
> names, or other parts of the query.
### Example
```yaml
tools:
search-users:
@@ -43,41 +43,12 @@ tools:
statement: SELECT * FROM users WHERE name LIKE ? AND age >= ?
```
### Example with Template Parameters
> **Note:** This tool allows direct modifications to the SQL statement,
> including identifiers, column names, and table names. **This makes it more
> vulnerable to SQL injections**. Using basic parameters only (see above) is
> recommended for performance and safety reasons. For more details, please check
> [templateParameters](_index#template-parameters).
```yaml
tools:
list_table:
kind: sqlite-sql
source: my-sqlite-db
statement: |
SELECT * FROM {{.tableName}};
description: |
Use this tool to list all information from a specific table.
Example:
{{
"tableName": "flights",
}}
templateParameters:
- name: tableName
type: string
description: Table to select from
```
## Reference
| **field** | **type** | **required** | **description** |
|--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "sqlite-sql". |
| source | string | true | Name of the source the SQLite source configuration. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | The SQL statement to execute. |
| parameters | [parameters](_index#specifying-parameters) | false | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement. |
| templateParameters | [templateParameters](_index#template-parameters) | false | List of [templateParameters](_index#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
| **field** | **type** | **required** | **description** |
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
| kind | string | Yes | Must be "sqlite-sql" |
| source | string | Yes | Name of a SQLite source configuration |
| description | string | Yes | Description of what the tool does |
| parameters | array | No | List of parameters for the SQL statement |
| statement | string | Yes | The SQL statement to execute |

14
go.mod
View File

@@ -10,15 +10,15 @@ require (
cloud.google.com/go/bigtable v1.37.0
cloud.google.com/go/cloudsqlconn v1.17.2
cloud.google.com/go/spanner v1.82.0
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.29.0
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.52.0
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.28.0
github.com/couchbase/gocb/v2 v2.10.0
github.com/couchbase/tools-common/http v1.0.9
github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/chi/v5 v5.2.1
github.com/go-chi/httplog/v2 v2.1.1
github.com/go-chi/render v1.0.3
github.com/go-playground/validator/v10 v10.26.0
github.com/go-sql-driver/mysql v1.9.3
github.com/go-sql-driver/mysql v1.9.2
github.com/goccy/go-yaml v1.18.0
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
@@ -26,7 +26,7 @@ require (
github.com/json-iterator/go v1.1.12
github.com/microsoft/go-mssqldb v1.8.2
github.com/neo4j/neo4j-go-driver/v5 v5.28.1
github.com/redis/go-redis/v9 v9.10.0
github.com/redis/go-redis/v9 v9.9.0
github.com/spf13/cobra v1.9.1
github.com/valkey-io/valkey-go v1.0.61
go.opentelemetry.io/contrib/propagators/autoprop v0.61.0
@@ -38,7 +38,7 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.36.0
go.opentelemetry.io/otel/trace v1.36.0
golang.org/x/oauth2 v0.30.0
google.golang.org/api v0.238.0
google.golang.org/api v0.236.0
modernc.org/sqlite v1.38.0
)
@@ -58,7 +58,7 @@ require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.52.0 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/apache/arrow/go/v15 v15.0.2 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect

32
go.sum
View File

@@ -651,14 +651,14 @@ github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 h1:DBjmt6/otSdULyJdVg2
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.29.0 h1:YVtMlmfRUTaWs3+1acwMBp7rBUo6zrxl6Kn13/R9YW4=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.29.0/go.mod h1:rKOFVIPbNs2wZeh7ZeQ0D9p/XLgbNiTr5m7x6KuAshk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.52.0 h1:QFgWzcdmJlgEAwJz/zePYVJQxfoJGRtgIqZfIUFg5oQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.52.0/go.mod h1:ayYHuYU7iNcNtEs1K9k6D/Bju7u1VEHMQm5qQ1n3GtM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.28.0 h1:RC78FvsZ8rLRLgVQuw1jMJ8d6t38QgOv3hDoUVGD50U=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.28.0/go.mod h1:03+QlJ+6zSrBaVaZ9K87fzUyKBDcAh0X1n1Vxq3XAjc=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.52.0 h1:0l8ynskVvq1dvIn5vJbFMf/a/3TqFpRmCMrruFbzlvk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.52.0/go.mod h1:f/ad5NuHnYz8AOZGuR0cY+l36oSCstdxD73YlIchr6I=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.52.0 h1:wbMd4eG/fOhsCa6+IP8uEDvWF5vl7rNoUWmP5f72Tbs=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.52.0/go.mod h1:gdIm9TxRk5soClCwuB0FtdXsbqtw0aqPwBEurK9tPkw=
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
@@ -768,8 +768,8 @@ github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzP
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/httplog/v2 v2.1.1 h1:ojojiu4PIaoeJ/qAO4GWUxJqvYUTobeo7zmuHQJAxRk=
github.com/go-chi/httplog/v2 v2.1.1/go.mod h1:/XXdxicJsp4BA5fapgIC3VuTD+z0Z/VzukoB3VDc1YE=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
@@ -803,8 +803,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
@@ -1051,8 +1051,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs=
github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/redis/go-redis/v9 v9.9.0 h1:URbPQ4xVQSQhZ27WMQVmZSo3uT3pL+4IdHVcYq2nVfM=
github.com/redis/go-redis/v9 v9.9.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -1605,8 +1605,8 @@ google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/
google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI=
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.238.0 h1:+EldkglWIg/pWjkq97sd+XxH7PxakNYoe/rkSTbnvOs=
google.golang.org/api v0.238.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
google.golang.org/api v0.236.0 h1:CAiEiDVtO4D/Qja2IA9VzlFrgPnK3XVMmRoJZlSWbc0=
google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

View File

@@ -18,7 +18,6 @@ import (
"context"
"database/sql"
"fmt"
"net/url"
"slices"
"cloud.google.com/go/cloudsqlconn/sqlserver/mssql"
@@ -112,13 +111,7 @@ func initCloudSQLMssqlConnection(ctx context.Context, tracer trace.Tracer, name,
defer span.End()
// Create dsn
query := fmt.Sprintf("database=%s&cloudsql=%s:%s:%s", dbname, project, region, instance)
url := &url.URL{
Scheme: "sqlserver",
User: url.UserPassword(user, pass),
Host: ipAddress,
RawQuery: query,
}
dsn := fmt.Sprintf("sqlserver://%s:%s@%s?database=%s&cloudsql=%s:%s:%s", user, pass, ipAddress, dbname, project, region, instance)
// Get dial options
userAgent, err := util.UserAgentFromContext(ctx)
@@ -141,7 +134,7 @@ func initCloudSQLMssqlConnection(ctx context.Context, tracer trace.Tracer, name,
// Open database connection
db, err := sql.Open(
"cloudsql-sqlserver-driver",
url.String(),
dsn,
)
if err != nil {
return nil, err

View File

@@ -15,7 +15,6 @@ package http
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"net/url"
@@ -23,7 +22,6 @@ import (
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/util"
"go.opentelemetry.io/otel/trace"
)
@@ -47,13 +45,12 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources
}
type Config struct {
Name string `yaml:"name" validate:"required"`
Kind string `yaml:"kind" validate:"required"`
BaseURL string `yaml:"baseUrl"`
Timeout string `yaml:"timeout"`
DefaultHeaders map[string]string `yaml:"headers"`
QueryParams map[string]string `yaml:"queryParams"`
DisableSslVerification bool `yaml:"disableSslVerification"`
Name string `yaml:"name" validate:"required"`
Kind string `yaml:"kind" validate:"required"`
BaseURL string `yaml:"baseUrl"`
Timeout string `yaml:"timeout"`
DefaultHeaders map[string]string `yaml:"headers"`
QueryParams map[string]string `yaml:"queryParams"`
}
func (r Config) SourceConfigKind() string {
@@ -66,25 +63,8 @@ func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.So
if err != nil {
return nil, fmt.Errorf("unable to parse Timeout string as time.Duration: %s", err)
}
tr := &http.Transport{}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
}
if r.DisableSslVerification {
tr.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
logger.WarnContext(ctx, "Insecure HTTP is enabled for HTTP source %s. TLS certificate verification is skipped.\n", r.Name)
}
client := http.Client{
Timeout: duration,
Transport: tr,
Timeout: duration,
}
// Validate BaseURL

View File

@@ -34,24 +34,6 @@ func TestParseFromYamlHttp(t *testing.T) {
{
desc: "basic example",
in: `
sources:
my-http-instance:
kind: http
baseUrl: http://test_server/
`,
want: map[string]sources.SourceConfig{
"my-http-instance": http.Config{
Name: "my-http-instance",
Kind: http.SourceKind,
BaseURL: "http://test_server/",
Timeout: "30s",
DisableSslVerification: false,
},
},
},
{
desc: "advanced example",
in: `
sources:
my-http-instance:
kind: http
@@ -63,17 +45,15 @@ func TestParseFromYamlHttp(t *testing.T) {
queryParams:
api-key: test_api_key
param: param-value
disableSslVerification: true
`,
want: map[string]sources.SourceConfig{
"my-http-instance": http.Config{
Name: "my-http-instance",
Kind: http.SourceKind,
BaseURL: "http://test_server/",
Timeout: "10s",
DefaultHeaders: map[string]string{"Authorization": "test_header", "Custom-Header": "custom"},
QueryParams: map[string]string{"api-key": "test_api_key", "param": "param-value"},
DisableSslVerification: true,
Name: "my-http-instance",
Kind: http.SourceKind,
BaseURL: "http://test_server/",
Timeout: "10s",
DefaultHeaders: map[string]string{"Authorization": "test_header", "Custom-Header": "custom"},
QueryParams: map[string]string{"api-key": "test_api_key", "param": "param-value"},
},
},
},

View File

@@ -18,7 +18,6 @@ import (
"context"
"database/sql"
"fmt"
"net/url"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
@@ -107,17 +106,10 @@ func initMssqlConnection(ctx context.Context, tracer trace.Tracer, name, host, p
defer span.End()
// Create dsn
query := url.Values{}
query.Add("database", dbname)
url := &url.URL{
Scheme: "sqlserver",
User: url.UserPassword(user, pass),
Host: fmt.Sprintf("%s:%s", host, port),
RawQuery: query.Encode(),
}
dsn := fmt.Sprintf("sqlserver://%s:%s@%s:%s?database=%s", user, pass, host, port, dbname)
// Open database connection
db, err := sql.Open("sqlserver", url.String())
db, err := sql.Open("sqlserver", dsn)
if err != nil {
return nil, fmt.Errorf("sql.Open: %w", err)
}

View File

@@ -17,7 +17,6 @@ package postgres
import (
"context"
"fmt"
"net/url"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
@@ -97,15 +96,9 @@ func initPostgresConnectionPool(ctx context.Context, tracer trace.Tracer, name,
//nolint:all // Reassigned ctx
ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name)
defer span.End()
// urlExample := "postgres:dd//username:password@localhost:5432/database_name"
url := &url.URL{
Scheme: "postgres",
User: url.UserPassword(user, pass),
Host: fmt.Sprintf("%s:%s", host, port),
Path: dbname,
}
pool, err := pgxpool.New(ctx, url.String())
i := fmt.Sprintf("postgres://%s:%s@%s:%s/%s", user, pass, host, port, dbname)
pool, err := pgxpool.New(ctx, i)
if err != nil {
return nil, fmt.Errorf("unable to create connection pool: %w", err)
}

View File

@@ -53,14 +53,13 @@ var _ compatibleSource = &bigqueryds.Source{}
var compatibleSources = [...]string{bigqueryds.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"`
Statement string `yaml:"statement" validate:"required"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
TemplateParameters tools.Parameters `yaml:"templateParameters"`
Name string `yaml:"name" validate:"required"`
Kind string `yaml:"kind" validate:"required"`
Source string `yaml:"source" validate:"required"`
Description string `yaml:"description" validate:"required"`
Statement string `yaml:"statement" validate:"required"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
}
// validate interface
@@ -83,26 +82,22 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
allParameters, paramManifest, paramMcpManifest := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: paramMcpManifest,
InputSchema: cfg.Parameters.McpManifest(),
}
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: kind,
Parameters: cfg.Parameters,
TemplateParameters: cfg.TemplateParameters,
AllParams: allParameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,
Client: s.BigQueryClient(),
manifest: tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
Name: cfg.Name,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,
Client: s.BigQueryClient(),
manifest: tools.Manifest{Description: cfg.Description, Parameters: cfg.Parameters.Manifest(), AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
}
return t, nil
}
@@ -111,12 +106,10 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
TemplateParameters tools.Parameters `yaml:"templateParameters"`
AllParams tools.Parameters `yaml:"allParams"`
Name string `yaml:"name"`
Kind string `yaml:"kind"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
Client *bigqueryapi.Client
Statement string
@@ -125,22 +118,11 @@ type Tool struct {
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) ([]any, error) {
paramsMap := params.AsMap()
newStatement, err := tools.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract template params %w", err)
}
newParams, err := tools.GetParams(t.Parameters, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
}
namedArgs := make([]bigqueryapi.QueryParameter, 0, len(newParams))
newParamsMap := newParams.AsReversedMap()
for _, v := range newParams.AsSlice() {
paramName := newParamsMap[v]
if strings.Contains(newStatement, "@"+paramName) {
namedArgs := make([]bigqueryapi.QueryParameter, 0, len(params))
paramsMap := params.AsReversedMap()
for _, v := range params.AsSlice() {
paramName := paramsMap[v]
if strings.Contains(t.Statement, "@"+paramName) {
namedArgs = append(namedArgs, bigqueryapi.QueryParameter{
Name: paramName,
Value: v,
@@ -152,7 +134,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) ([]any, erro
}
}
query := t.Client.Query(newStatement)
query := t.Client.Query(t.Statement)
query.Parameters = namedArgs
query.Location = t.Client.Location
@@ -182,7 +164,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) ([]any, erro
}
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
return tools.ParseParams(t.AllParams, data, claims)
return tools.ParseParams(t.Parameters, data, claims)
}
func (t Tool) Manifest() tools.Manifest {

View File

@@ -82,76 +82,3 @@ func TestParseFromYamlBigQuery(t *testing.T) {
}
}
func TestParseFromYamlWithTemplateBigQuery(t *testing.T) {
ctx, err := testutils.ContextWithNewLogger()
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
tcs := []struct {
desc string
in string
want server.ToolConfigs
}{
{
desc: "basic example",
in: `
tools:
example_tool:
kind: bigquery-sql
source: my-instance
description: some description
statement: |
SELECT * FROM SQL_STATEMENT;
parameters:
- name: country
type: string
description: some description
templateParameters:
- name: tableName
type: string
description: The table to select hotels from.
- name: fieldArray
type: array
description: The columns to return for the query.
items:
name: column
type: string
description: A column name that will be returned from the query.
`,
want: server.ToolConfigs{
"example_tool": bigquery.Config{
Name: "example_tool",
Kind: "bigquery-sql",
Source: "my-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",
AuthRequired: []string{},
Parameters: []tools.Parameter{
tools.NewStringParameter("country", "some description"),
},
TemplateParameters: []tools.Parameter{
tools.NewStringParameter("tableName", "The table to select hotels from."),
tools.NewArrayParameter("fieldArray", "The columns to return for the query.", tools.NewStringParameter("column", "A column name that will be returned from the query.")),
},
},
},
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got := struct {
Tools server.ToolConfigs `yaml:"tools"`
}{}
// Parse contents
err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
if err != nil {
t.Fatalf("unable to unmarshal: %s", err)
}
if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
t.Fatalf("incorrect parse: diff %v", diff)
}
})
}
}

View File

@@ -51,14 +51,13 @@ var _ compatibleSource = &bigtabledb.Source{}
var compatibleSources = [...]string{bigtabledb.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"`
Statement string `yaml:"statement" validate:"required"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
TemplateParameters tools.Parameters `yaml:"templateParameters"`
Name string `yaml:"name" validate:"required"`
Kind string `yaml:"kind" validate:"required"`
Source string `yaml:"source" validate:"required"`
Description string `yaml:"description" validate:"required"`
Statement string `yaml:"statement" validate:"required"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
}
// validate interface
@@ -81,26 +80,22 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
allParameters, paramManifest, paramMcpManifest := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: paramMcpManifest,
InputSchema: cfg.Parameters.McpManifest(),
}
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: kind,
Parameters: cfg.Parameters,
TemplateParameters: cfg.TemplateParameters,
AllParams: allParameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,
Client: s.BigtableClient(),
manifest: tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
Name: cfg.Name,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,
Client: s.BigtableClient(),
manifest: tools.Manifest{Description: cfg.Description, Parameters: cfg.Parameters.Manifest(), AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
}
return t, nil
}
@@ -109,12 +104,10 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
TemplateParameters tools.Parameters `yaml:"templateParameters"`
AllParams tools.Parameters `yaml:"allParams"`
Name string `yaml:"name"`
Kind string `yaml:"kind"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
Client *bigtable.Client
Statement string
@@ -148,32 +141,21 @@ func getMapParamsType(tparams tools.Parameters, params tools.ParamValues) (map[s
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) ([]any, error) {
paramsMap := params.AsMap()
newStatement, err := tools.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract template params %w", err)
}
newParams, err := tools.GetParams(t.Parameters, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
}
mapParamsType, err := getMapParamsType(t.Parameters, newParams)
mapParamsType, err := getMapParamsType(t.Parameters, params)
if err != nil {
return nil, fmt.Errorf("fail to get map params: %w", err)
}
ps, err := t.Client.PrepareStatement(
ctx,
newStatement,
t.Statement,
mapParamsType,
)
if err != nil {
return nil, fmt.Errorf("unable to prepare statement: %w", err)
}
bs, err := ps.Bind(newParams.AsMap())
bs, err := ps.Bind(params.AsMap())
if err != nil {
return nil, fmt.Errorf("unable to bind: %w", err)
}
@@ -201,7 +183,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) ([]any, erro
}
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
return tools.ParseParams(t.AllParams, data, claims)
return tools.ParseParams(t.Parameters, data, claims)
}
func (t Tool) Manifest() tools.Manifest {

View File

@@ -82,76 +82,3 @@ func TestParseFromYamlBigtable(t *testing.T) {
}
}
func TestParseFromYamlWithTemplateBigtable(t *testing.T) {
ctx, err := testutils.ContextWithNewLogger()
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
tcs := []struct {
desc string
in string
want server.ToolConfigs
}{
{
desc: "basic example",
in: `
tools:
example_tool:
kind: bigtable-sql
source: my-pg-instance
description: some description
statement: |
SELECT * FROM SQL_STATEMENT;
parameters:
- name: country
type: string
description: some description
templateParameters:
- name: tableName
type: string
description: The table to select hotels from.
- name: fieldArray
type: array
description: The columns to return for the query.
items:
name: column
type: string
description: A column name that will be returned from the query.
`,
want: server.ToolConfigs{
"example_tool": bigtable.Config{
Name: "example_tool",
Kind: "bigtable-sql",
Source: "my-pg-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",
AuthRequired: []string{},
Parameters: []tools.Parameter{
tools.NewStringParameter("country", "some description"),
},
TemplateParameters: []tools.Parameter{
tools.NewStringParameter("tableName", "The table to select hotels from."),
tools.NewArrayParameter("fieldArray", "The columns to return for the query.", tools.NewStringParameter("column", "A column name that will be returned from the query.")),
},
},
},
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got := struct {
Tools server.ToolConfigs `yaml:"tools"`
}{}
// Parse contents
err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
if err != nil {
t.Fatalf("unable to unmarshal: %s", err)
}
if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
t.Fatalf("incorrect parse: diff %v", diff)
}
})
}
}

View File

@@ -53,14 +53,13 @@ var _ compatibleSource = &couchbase.Source{}
var compatibleSources = [...]string{couchbase.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"`
Statement string `yaml:"statement" validate:"required"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
TemplateParameters tools.Parameters `yaml:"templateParameters"`
Name string `yaml:"name" validate:"required"`
Kind string `yaml:"kind" validate:"required"`
Source string `yaml:"source" validate:"required"`
Description string `yaml:"description" validate:"required"`
Statement string `yaml:"statement" validate:"required"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
}
// validate interface
@@ -83,25 +82,21 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
allParameters, paramManifest, paramMcpManifest := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: paramMcpManifest,
InputSchema: cfg.Parameters.McpManifest(),
}
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: kind,
Parameters: cfg.Parameters,
TemplateParameters: cfg.TemplateParameters,
AllParams: allParameters,
Statement: cfg.Statement,
Scope: s.CouchbaseScope(),
QueryScanConsistency: s.CouchbaseQueryScanConsistency(),
AuthRequired: cfg.AuthRequired,
manifest: tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
manifest: tools.Manifest{Description: cfg.Description, Parameters: cfg.Parameters.Manifest(), AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
}
return t, nil
@@ -111,12 +106,10 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Parameters tools.Parameters `yaml:"parameters"`
TemplateParameters tools.Parameters `yaml:"templateParameters"`
AllParams tools.Parameters `yaml:"allParams"`
AuthRequired []string `yaml:"authRequired"`
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Parameters tools.Parameters `yaml:"parameters"`
AuthRequired []string `yaml:"authRequired"`
Scope *gocb.Scope
QueryScanConsistency uint
@@ -126,19 +119,10 @@ type Tool struct {
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) ([]any, error) {
namedParamsMap := params.AsMap()
newStatement, err := tools.ResolveTemplateParams(t.TemplateParameters, t.Statement, namedParamsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract template params %w", err)
}
newParams, err := tools.GetParams(t.Parameters, namedParamsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
}
results, err := t.Scope.Query(newStatement, &gocb.QueryOptions{
namedParams := params.AsMap()
results, err := t.Scope.Query(t.Statement, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistency(t.QueryScanConsistency),
NamedParameters: newParams.AsMap(),
NamedParameters: namedParams,
})
if err != nil {
return nil, fmt.Errorf("unable to execute query: %w", err)
@@ -157,7 +141,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) ([]any, erro
}
func (t Tool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (tools.ParamValues, error) {
return tools.ParseParams(t.AllParams, data, claimsMap)
return tools.ParseParams(t.Parameters, data, claimsMap)
}
func (t Tool) Manifest() tools.Manifest {

View File

@@ -85,67 +85,3 @@ func TestParseFromYamlCouchbase(t *testing.T) {
})
}
}
func TestParseFromYamlWithTemplateMssql(t *testing.T) {
ctx, err := testutils.ContextWithNewLogger()
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
tcs := []struct {
desc string
in string
want server.ToolConfigs
}{
{
desc: "basic example",
in: `
tools:
example_tool:
kind: couchbase-sql
source: my-couchbase-instance
description: some tool description
statement: |
select * from {{.tableName}} WHERE name = $hotel;
parameters:
- name: hotel
type: string
description: hotel parameter description
templateParameters:
- name: tableName
type: string
description: The table to select hotels from.
`,
want: server.ToolConfigs{
"example_tool": couchbase.Config{
Name: "example_tool",
Kind: "couchbase-sql",
AuthRequired: []string{},
Source: "my-couchbase-instance",
Description: "some tool description",
Statement: "select * from {{.tableName}} WHERE name = $hotel;\n",
Parameters: []tools.Parameter{
tools.NewStringParameter("hotel", "hotel parameter description"),
},
TemplateParameters: []tools.Parameter{
tools.NewStringParameter("tableName", "The table to select hotels from."),
},
},
},
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got := struct {
Tools server.ToolConfigs `yaml:"tools"`
}{}
// Parse contents
err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
if err != nil {
t.Fatalf("unable to unmarshal: %s", err)
}
if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
t.Fatalf("incorrect parse: diff %v", diff)
}
})
}
}

View File

@@ -59,7 +59,6 @@ type Config struct {
Method tools.HTTPMethod `yaml:"method" validate:"required"`
Headers map[string]string `yaml:"headers"`
RequestBody string `yaml:"requestBody"`
PathParams tools.Parameters `yaml:"pathParams"`
QueryParams tools.Parameters `yaml:"queryParams"`
BodyParams tools.Parameters `yaml:"bodyParams"`
HeaderParams tools.Parameters `yaml:"headerParams"`
@@ -85,6 +84,20 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be `http`", kind)
}
// Create URL based on BaseURL and Path
// Attach query parameters
u, err := url.Parse(s.BaseURL + cfg.Path)
if err != nil {
return nil, fmt.Errorf("error parsing URL: %s", err)
}
// Get existing query parameters from the URL
queryParameters := u.Query()
for key, value := range s.QueryParams {
queryParameters.Add(key, value)
}
u.RawQuery = queryParameters.Encode()
// Combine Source and Tool headers.
// In case of conflict, Tool header overrides Source header
combinedHeaders := make(map[string]string)
@@ -92,11 +105,10 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
maps.Copy(combinedHeaders, cfg.Headers)
// Create a slice for all parameters
allParameters := slices.Concat(cfg.PathParams, cfg.BodyParams, cfg.HeaderParams, cfg.QueryParams)
allParameters := slices.Concat(cfg.BodyParams, cfg.HeaderParams, cfg.QueryParams)
// Create parameter MCP manifest
paramManifest := slices.Concat(
cfg.PathParams.Manifest(),
cfg.QueryParams.Manifest(),
cfg.BodyParams.Manifest(),
cfg.HeaderParams.Manifest(),
@@ -104,14 +116,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
if paramManifest == nil {
paramManifest = make([]tools.ParameterManifest, 0)
}
pathMcpManifest := cfg.PathParams.McpManifest()
queryMcpManifest := cfg.QueryParams.McpManifest()
bodyMcpManifest := cfg.BodyParams.McpManifest()
headerMcpManifest := cfg.HeaderParams.McpManifest()
// Concatenate parameters for MCP `required` field
concatRequiredManifest := slices.Concat(
pathMcpManifest.Required,
queryMcpManifest.Required,
bodyMcpManifest.Required,
headerMcpManifest.Required,
@@ -122,9 +133,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// Concatenate parameters for MCP `properties` field
concatPropertiesManifest := make(map[string]tools.ParameterMcpManifest)
for name, p := range pathMcpManifest.Properties {
concatPropertiesManifest[name] = p
}
for name, p := range queryMcpManifest.Properties {
concatPropertiesManifest[name] = p
}
@@ -159,23 +167,20 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
BaseURL: s.BaseURL,
Path: cfg.Path,
Method: cfg.Method,
AuthRequired: cfg.AuthRequired,
RequestBody: cfg.RequestBody,
PathParams: cfg.PathParams,
QueryParams: cfg.QueryParams,
BodyParams: cfg.BodyParams,
HeaderParams: cfg.HeaderParams,
Headers: combinedHeaders,
DefaultQueryParams: s.QueryParams,
Client: s.Client,
AllParams: allParameters,
manifest: tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
Name: cfg.Name,
Kind: kind,
URL: u,
Method: cfg.Method,
AuthRequired: cfg.AuthRequired,
RequestBody: cfg.RequestBody,
QueryParams: cfg.QueryParams,
BodyParams: cfg.BodyParams,
HeaderParams: cfg.HeaderParams,
Headers: combinedHeaders,
Client: s.Client,
AllParams: allParameters,
manifest: tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
}, nil
}
@@ -188,18 +193,14 @@ type Tool struct {
Description string `yaml:"description"`
AuthRequired []string `yaml:"authRequired"`
BaseURL string `yaml:"baseURL"`
Path string `yaml:"path"`
Method tools.HTTPMethod `yaml:"method"`
Headers map[string]string `yaml:"headers"`
DefaultQueryParams map[string]string `yaml:"defaultQueryParams"`
RequestBody string `yaml:"requestBody"`
PathParams tools.Parameters `yaml:"pathParams"`
QueryParams tools.Parameters `yaml:"queryParams"`
BodyParams tools.Parameters `yaml:"bodyParams"`
HeaderParams tools.Parameters `yaml:"headerParams"`
AllParams tools.Parameters `yaml:"allParams"`
URL *url.URL `yaml:"url"`
Method tools.HTTPMethod `yaml:"method"`
Headers map[string]string `yaml:"headers"`
RequestBody string `yaml:"requestBody"`
QueryParams tools.Parameters `yaml:"queryParams"`
BodyParams tools.Parameters `yaml:"bodyParams"`
HeaderParams tools.Parameters `yaml:"headerParams"`
AllParams tools.Parameters `yaml:"allParams"`
Client *http.Client
manifest tools.Manifest
@@ -240,45 +241,14 @@ func getRequestBody(bodyParams tools.Parameters, requestBodyPayload string, para
}
// Helper function to generate the HTTP request URL upon Tool invocation.
func getURL(baseURL, path string, pathParams, queryParams tools.Parameters, defaultQueryParams map[string]string, paramsMap map[string]any) (string, error) {
// use Go template to replace path params
pathParamValues, err := tools.GetParams(pathParams, paramsMap)
if err != nil {
return "", err
}
pathParamsMap := pathParamValues.AsMap()
templ, err := template.New("url").Parse(path)
if err != nil {
return "", fmt.Errorf("error parsing URL: %s", err)
}
var templatedPath bytes.Buffer
err = templ.Execute(&templatedPath, pathParamsMap)
if err != nil {
return "", fmt.Errorf("error replacing pathParams: %s", err)
}
// Create URL based on BaseURL and Path
// Attach query parameters
parsedURL, err := url.Parse(baseURL + templatedPath.String())
if err != nil {
return "", fmt.Errorf("error parsing URL: %s", err)
}
// Get existing query parameters from the URL
queryParameters := parsedURL.Query()
for key, value := range defaultQueryParams {
queryParameters.Add(key, value)
}
parsedURL.RawQuery = queryParameters.Encode()
func getURL(u *url.URL, queryParams tools.Parameters, paramsMap map[string]any) (string, error) {
// Set dynamic query parameters
query := parsedURL.Query()
query := u.Query()
for _, p := range queryParams {
query.Add(p.GetName(), fmt.Sprintf("%v", paramsMap[p.GetName()]))
}
parsedURL.RawQuery = query.Encode()
return parsedURL.String(), nil
u.RawQuery = query.Encode()
return u.String(), nil
}
// Helper function to generate the HTTP headers upon Tool invocation.
@@ -309,9 +279,9 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues) ([]any, erro
}
// Calculate URL
urlString, err := getURL(t.BaseURL, t.Path, t.PathParams, t.QueryParams, t.DefaultQueryParams, paramsMap)
urlString, err := getURL(t.URL, t.QueryParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("error populating path parameters: %s", err)
return nil, fmt.Errorf("error populating query parameters: %s", err)
}
req, _ := http.NewRequest(string(t.Method), urlString, strings.NewReader(requestBody))

View File

@@ -44,30 +44,7 @@ func TestParseFromYamlHTTP(t *testing.T) {
kind: http
source: my-instance
method: GET
description: some description
path: search
`,
want: server.ToolConfigs{
"example_tool": http.Config{
Name: "example_tool",
Kind: "http",
Source: "my-instance",
Method: "GET",
Path: "search",
Description: "some description",
AuthRequired: []string{},
},
},
},
{
desc: "advanced example",
in: `
tools:
example_tool:
kind: http
source: my-instance
method: GET
path: "{{.pathParam}}?name=alice&pet=cat"
path: "search?name=alice&pet=cat"
description: some description
authRequired:
- my-google-auth-service
@@ -81,10 +58,6 @@ func TestParseFromYamlHTTP(t *testing.T) {
field: user_id
- name: other-auth-service
field: user_id
pathParams:
- name: pathParam
type: string
description: path param
requestBody: |
{
"age": {{.age}},
@@ -112,7 +85,7 @@ func TestParseFromYamlHTTP(t *testing.T) {
Kind: "http",
Source: "my-instance",
Method: "GET",
Path: "{{.pathParam}}?name=alice&pet=cat",
Path: "search?name=alice&pet=cat",
Description: "some description",
AuthRequired: []string{"my-google-auth-service", "other-auth-service"},
QueryParams: []tools.Parameter{
@@ -120,11 +93,6 @@ func TestParseFromYamlHTTP(t *testing.T) {
[]tools.ParamAuthService{{Name: "my-google-auth-service", Field: "user_id"},
{Name: "other-auth-service", Field: "user_id"}}),
},
PathParams: tools.Parameters{
&tools.StringParameter{
CommonParameter: tools.CommonParameter{Name: "pathParam", Type: "string", Desc: "path param"},
},
},
RequestBody: `{
"age": {{.age}},
"city": "{{.city}}",

View File

@@ -130,10 +130,7 @@ func ParseParams(ps Parameters, data map[string]any, claimsMap map[string]map[st
var ok bool
v, ok = data[name]
if !ok {
v = p.GetDefault()
if v == nil {
return nil, fmt.Errorf("parameter %q is required", name)
}
return nil, fmt.Errorf("parameter %q is required", name)
}
} else {
// parse authenticated parameter
@@ -256,7 +253,6 @@ type Parameter interface {
// but this is done to differentiate it from the fields in CommonParameter.
GetName() string
GetType() string
GetDefault() any
GetAuthServices() []ParamAuthService
Parse(any) (any, error)
Manifest() ParameterManifest
@@ -386,10 +382,8 @@ func (ps Parameters) McpManifest() McpToolsSchema {
for _, p := range ps {
name := p.GetName()
properties[name] = p.McpManifest()
// parameters that doesn't have a default value are added to the required field
if p.GetDefault() == nil {
required = append(required, name)
}
// all parameters are added to the required field
required = append(required, name)
}
return McpToolsSchema{
@@ -403,7 +397,6 @@ func (ps Parameters) McpManifest() McpToolsSchema {
type ParameterManifest struct {
Name string `json:"name"`
Type string `json:"type"`
Required bool `json:"required"`
Description string `json:"description"`
AuthServices []string `json:"authSources"`
Items *ParameterManifest `json:"items,omitempty"`
@@ -420,7 +413,6 @@ type ParameterMcpManifest struct {
type CommonParameter struct {
Name string `yaml:"name" validate:"required"`
Type string `yaml:"type" validate:"required"`
Default any `yaml:"default"`
Desc string `yaml:"description" validate:"required"`
AuthServices []ParamAuthService `yaml:"authServices"`
AuthSources []ParamAuthService `yaml:"authSources"` // Deprecated: Kept for compatibility.
@@ -436,10 +428,6 @@ func (p *CommonParameter) GetType() string {
return p.Type
}
func (p *CommonParameter) GetDefault() any {
return p.Default
}
// Manifest returns the manifest for the Parameter.
func (p *CommonParameter) Manifest() ParameterManifest {
// only list ParamAuthService names (without fields) in manifest
@@ -447,14 +435,9 @@ func (p *CommonParameter) Manifest() ParameterManifest {
for i, a := range p.AuthServices {
authNames[i] = a.Name
}
var required bool
if p.Default == nil {
required = true
}
return ParameterManifest{
Name: p.Name,
Type: p.Type,
Required: required,
Description: p.Desc,
AuthServices: authNames,
}
@@ -485,7 +468,7 @@ type ParamAuthService struct {
}
// NewStringParameter is a convenience function for initializing a StringParameter.
func NewStringParameter(name string, desc string) *StringParameter {
func NewStringParameter(name, desc string) *StringParameter {
return &StringParameter{
CommonParameter: CommonParameter{
Name: name,
@@ -496,21 +479,8 @@ func NewStringParameter(name string, desc string) *StringParameter {
}
}
// NewStringParameterWithDefault is a convenience function for initializing a StringParameter with default value.
func NewStringParameterWithDefault(name string, defaultV, desc string) *StringParameter {
return &StringParameter{
CommonParameter: CommonParameter{
Name: name,
Type: typeString,
Default: defaultV,
Desc: desc,
AuthServices: nil,
},
}
}
// NewStringParameterWithAuth is a convenience function for initializing a StringParameter with a list of ParamAuthService.
func NewStringParameterWithAuth(name string, desc string, authServices []ParamAuthService) *StringParameter {
func NewStringParameterWithAuth(name, desc string, authServices []ParamAuthService) *StringParameter {
return &StringParameter{
CommonParameter: CommonParameter{
Name: name,
@@ -541,7 +511,7 @@ func (p *StringParameter) GetAuthServices() []ParamAuthService {
}
// NewIntParameter is a convenience function for initializing a IntParameter.
func NewIntParameter(name string, desc string) *IntParameter {
func NewIntParameter(name, desc string) *IntParameter {
return &IntParameter{
CommonParameter: CommonParameter{
Name: name,
@@ -552,21 +522,8 @@ func NewIntParameter(name string, desc string) *IntParameter {
}
}
// NewIntParameterWithDefault is a convenience function for initializing a IntParameter with default value.
func NewIntParameterWithDefault(name string, defaultV any, desc string) *IntParameter {
return &IntParameter{
CommonParameter: CommonParameter{
Name: name,
Type: typeInt,
Default: defaultV,
Desc: desc,
AuthServices: nil,
},
}
}
// NewIntParameterWithAuth is a convenience function for initializing a IntParameter with a list of ParamAuthService.
func NewIntParameterWithAuth(name string, desc string, authServices []ParamAuthService) *IntParameter {
func NewIntParameterWithAuth(name, desc string, authServices []ParamAuthService) *IntParameter {
return &IntParameter{
CommonParameter: CommonParameter{
Name: name,
@@ -610,7 +567,7 @@ func (p *IntParameter) GetAuthServices() []ParamAuthService {
}
// NewFloatParameter is a convenience function for initializing a FloatParameter.
func NewFloatParameter(name string, desc string) *FloatParameter {
func NewFloatParameter(name, desc string) *FloatParameter {
return &FloatParameter{
CommonParameter: CommonParameter{
Name: name,
@@ -621,21 +578,8 @@ func NewFloatParameter(name string, desc string) *FloatParameter {
}
}
// NewFloatParameterWithDefault is a convenience function for initializing a FloatParameter with default value.
func NewFloatParameterWithDefault(name string, defaultV float64, desc string) *FloatParameter {
return &FloatParameter{
CommonParameter: CommonParameter{
Name: name,
Type: typeFloat,
Default: defaultV,
Desc: desc,
AuthServices: nil,
},
}
}
// NewFloatParameterWithAuth is a convenience function for initializing a FloatParameter with a list of ParamAuthService.
func NewFloatParameterWithAuth(name string, desc string, authServices []ParamAuthService) *FloatParameter {
func NewFloatParameterWithAuth(name, desc string, authServices []ParamAuthService) *FloatParameter {
return &FloatParameter{
CommonParameter: CommonParameter{
Name: name,
@@ -677,7 +621,7 @@ func (p *FloatParameter) GetAuthServices() []ParamAuthService {
}
// NewBooleanParameter is a convenience function for initializing a BooleanParameter.
func NewBooleanParameter(name string, desc string) *BooleanParameter {
func NewBooleanParameter(name, desc string) *BooleanParameter {
return &BooleanParameter{
CommonParameter: CommonParameter{
Name: name,
@@ -688,21 +632,8 @@ func NewBooleanParameter(name string, desc string) *BooleanParameter {
}
}
// NewBooleanParameterWithDefault is a convenience function for initializing a BooleanParameter with default value.
func NewBooleanParameterWithDefault(name string, defaultV bool, desc string) *BooleanParameter {
return &BooleanParameter{
CommonParameter: CommonParameter{
Name: name,
Type: typeBool,
Default: defaultV,
Desc: desc,
AuthServices: nil,
},
}
}
// NewBooleanParameterWithAuth is a convenience function for initializing a BooleanParameter with a list of ParamAuthService.
func NewBooleanParameterWithAuth(name string, desc string, authServices []ParamAuthService) *BooleanParameter {
func NewBooleanParameterWithAuth(name, desc string, authServices []ParamAuthService) *BooleanParameter {
return &BooleanParameter{
CommonParameter: CommonParameter{
Name: name,
@@ -733,7 +664,7 @@ func (p *BooleanParameter) GetAuthServices() []ParamAuthService {
}
// NewArrayParameter is a convenience function for initializing a ArrayParameter.
func NewArrayParameter(name string, desc string, items Parameter) *ArrayParameter {
func NewArrayParameter(name, desc string, items Parameter) *ArrayParameter {
return &ArrayParameter{
CommonParameter: CommonParameter{
Name: name,
@@ -745,22 +676,8 @@ func NewArrayParameter(name string, desc string, items Parameter) *ArrayParamete
}
}
// NewArrayParameterWithDefault is a convenience function for initializing a ArrayParameter with default value.
func NewArrayParameterWithDefault(name string, defaultV any, desc string, items Parameter) *ArrayParameter {
return &ArrayParameter{
CommonParameter: CommonParameter{
Name: name,
Type: typeArray,
Default: defaultV,
Desc: desc,
AuthServices: nil,
},
Items: items,
}
}
// NewArrayParameterWithAuth is a convenience function for initializing a ArrayParameter with a list of ParamAuthService.
func NewArrayParameterWithAuth(name string, desc string, items Parameter, authServices []ParamAuthService) *ArrayParameter {
func NewArrayParameterWithAuth(name, desc string, items Parameter, authServices []ParamAuthService) *ArrayParameter {
return &ArrayParameter{
CommonParameter: CommonParameter{
Name: name,
@@ -829,15 +746,9 @@ func (p *ArrayParameter) Manifest() ParameterManifest {
authNames[i] = a.Name
}
items := p.Items.Manifest()
var required bool
if p.Default == nil {
required = true
items.Required = true
}
return ParameterManifest{
Name: p.Name,
Type: p.Type,
Required: required,
Description: p.Desc,
AuthServices: authNames,
Items: &items,

View File

@@ -125,100 +125,6 @@ func TestParametersMarshal(t *testing.T) {
tools.NewArrayParameter("my_array", "this param is an array of floats", tools.NewFloatParameter("my_float", "float item")),
},
},
{
name: "string default",
in: []map[string]any{
{
"name": "my_string",
"type": "string",
"default": "foo",
"description": "this param is a string",
},
},
want: tools.Parameters{
tools.NewStringParameterWithDefault("my_string", "foo", "this param is a string"),
},
},
{
name: "int default",
in: []map[string]any{
{
"name": "my_integer",
"type": "integer",
"default": 5,
"description": "this param is an int",
},
},
want: tools.Parameters{
tools.NewIntParameterWithDefault("my_integer", uint64(5), "this param is an int"),
},
},
{
name: "float default",
in: []map[string]any{
{
"name": "my_float",
"type": "float",
"default": 1.1,
"description": "my param is a float",
},
},
want: tools.Parameters{
tools.NewFloatParameterWithDefault("my_float", 1.1, "my param is a float"),
},
},
{
name: "bool default",
in: []map[string]any{
{
"name": "my_bool",
"type": "boolean",
"default": true,
"description": "this param is a boolean",
},
},
want: tools.Parameters{
tools.NewBooleanParameterWithDefault("my_bool", true, "this param is a boolean"),
},
},
{
name: "string array default",
in: []map[string]any{
{
"name": "my_array",
"type": "array",
"default": `["foo", "bar"]`,
"description": "this param is an array of strings",
"items": map[string]string{
"name": "my_string",
"type": "string",
"description": "string item",
},
},
},
want: tools.Parameters{
tools.NewArrayParameterWithDefault("my_array", `["foo", "bar"]`, "this param is an array of strings", tools.NewStringParameter("my_string", "string item")),
},
},
{
name: "float array default",
in: []map[string]any{
{
"name": "my_array",
"type": "array",
"default": "[1.0, 1.1]",
"description": "this param is an array of floats",
"items": map[string]string{
"name": "my_float",
"type": "float",
"description": "float item",
},
},
},
want: tools.Parameters{
tools.NewArrayParameterWithDefault("my_array", "[1.0, 1.1]", "this param is an array of floats", tools.NewFloatParameter("my_float", "float item")),
},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
@@ -633,46 +539,6 @@ func TestParametersParse(t *testing.T) {
"my_bool": 1.5,
},
},
{
name: "string default",
params: tools.Parameters{
tools.NewStringParameterWithDefault("my_string", "foo", "this param is a string"),
},
in: map[string]any{},
want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: "foo"}},
},
{
name: "int default",
params: tools.Parameters{
tools.NewIntParameterWithDefault("my_int", 100, "this param is an int"),
},
in: map[string]any{},
want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 100}},
},
{
name: "int (big)",
params: tools.Parameters{
tools.NewIntParameterWithDefault("my_big_int", math.MaxInt64, "this param is an int"),
},
in: map[string]any{},
want: tools.ParamValues{tools.ParamValue{Name: "my_big_int", Value: math.MaxInt64}},
},
{
name: "float default",
params: tools.Parameters{
tools.NewFloatParameterWithDefault("my_float", 1.1, "this param is a float"),
},
in: map[string]any{},
want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 1.1}},
},
{
name: "bool default",
params: tools.Parameters{
tools.NewBooleanParameterWithDefault("my_bool", true, "this param is a bool"),
},
in: map[string]any{},
want: tools.ParamValues{tools.ParamValue{Name: "my_bool", Value: true}},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
@@ -942,22 +808,22 @@ func TestParamManifest(t *testing.T) {
{
name: "string",
in: tools.NewStringParameter("foo-string", "bar"),
want: tools.ParameterManifest{Name: "foo-string", Type: "string", Required: true, Description: "bar", AuthServices: []string{}},
want: tools.ParameterManifest{Name: "foo-string", Type: "string", Description: "bar", AuthServices: []string{}},
},
{
name: "int",
in: tools.NewIntParameter("foo-int", "bar"),
want: tools.ParameterManifest{Name: "foo-int", Type: "integer", Required: true, Description: "bar", AuthServices: []string{}},
want: tools.ParameterManifest{Name: "foo-int", Type: "integer", Description: "bar", AuthServices: []string{}},
},
{
name: "float",
in: tools.NewFloatParameter("foo-float", "bar"),
want: tools.ParameterManifest{Name: "foo-float", Type: "float", Required: true, Description: "bar", AuthServices: []string{}},
want: tools.ParameterManifest{Name: "foo-float", Type: "float", Description: "bar", AuthServices: []string{}},
},
{
name: "boolean",
in: tools.NewBooleanParameter("foo-bool", "bar"),
want: tools.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: true, Description: "bar", AuthServices: []string{}},
want: tools.ParameterManifest{Name: "foo-bool", Type: "boolean", Description: "bar", AuthServices: []string{}},
},
{
name: "array",
@@ -965,42 +831,9 @@ func TestParamManifest(t *testing.T) {
want: tools.ParameterManifest{
Name: "foo-array",
Type: "array",
Required: true,
Description: "bar",
AuthServices: []string{},
Items: &tools.ParameterManifest{Name: "foo-string", Type: "string", Required: true, Description: "bar", AuthServices: []string{}},
},
},
{
name: "string default",
in: tools.NewStringParameterWithDefault("foo-string", "foo", "bar"),
want: tools.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}},
},
{
name: "int default",
in: tools.NewIntParameterWithDefault("foo-int", 1, "bar"),
want: tools.ParameterManifest{Name: "foo-int", Type: "integer", Required: false, Description: "bar", AuthServices: []string{}},
},
{
name: "float default",
in: tools.NewFloatParameterWithDefault("foo-float", 1.1, "bar"),
want: tools.ParameterManifest{Name: "foo-float", Type: "float", Required: false, Description: "bar", AuthServices: []string{}},
},
{
name: "boolean default",
in: tools.NewBooleanParameterWithDefault("foo-bool", true, "bar"),
want: tools.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: false, Description: "bar", AuthServices: []string{}},
},
{
name: "array default",
in: tools.NewArrayParameterWithDefault("foo-array", `["foo", "bar"]`, "bar", tools.NewStringParameter("foo-string", "bar")),
want: tools.ParameterManifest{
Name: "foo-array",
Type: "array",
Required: false,
Description: "bar",
AuthServices: []string{},
Items: &tools.ParameterManifest{Name: "foo-string", Type: "string", Required: true, Description: "bar", AuthServices: []string{}},
Items: &tools.ParameterManifest{Name: "foo-string", Type: "string", Description: "bar", AuthServices: []string{}},
},
},
}
@@ -1060,54 +893,6 @@ func TestParamMcpManifest(t *testing.T) {
}
}
func TestMcpManifest(t *testing.T) {
tcs := []struct {
name string
in tools.Parameters
want tools.McpToolsSchema
}{
{
name: "string",
in: tools.Parameters{
tools.NewStringParameterWithDefault("foo-string", "foo", "bar"),
tools.NewStringParameter("foo-string2", "bar"),
tools.NewIntParameterWithDefault("foo-int", 1, "bar"),
tools.NewIntParameter("foo-int2", "bar"),
tools.NewArrayParameterWithDefault("foo-array", []string{"hello", "world"}, "bar", tools.NewStringParameter("foo-string", "bar")),
tools.NewArrayParameter("foo-array2", "bar", tools.NewStringParameter("foo-string", "bar")),
},
want: tools.McpToolsSchema{
Type: "object",
Properties: map[string]tools.ParameterMcpManifest{
"foo-string": tools.ParameterMcpManifest{Type: "string", Description: "bar"},
"foo-string2": tools.ParameterMcpManifest{Type: "string", Description: "bar"},
"foo-int": tools.ParameterMcpManifest{Type: "integer", Description: "bar"},
"foo-int2": tools.ParameterMcpManifest{Type: "integer", Description: "bar"},
"foo-array": tools.ParameterMcpManifest{
Type: "array",
Description: "bar",
Items: &tools.ParameterMcpManifest{Type: "string", Description: "bar"},
},
"foo-array2": tools.ParameterMcpManifest{
Type: "array",
Description: "bar",
Items: &tools.ParameterMcpManifest{Type: "string", Description: "bar"},
},
},
Required: []string{"foo-string2", "foo-int2", "foo-array2"},
},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
got := tc.in.McpManifest()
if !reflect.DeepEqual(got, tc.want) {
t.Fatalf("unexpected manifest: got %+v, want %+v", got, tc.want)
}
})
}
}
func TestFailParametersUnmarshal(t *testing.T) {
ctx, err := testutils.ContextWithNewLogger()
if err != nil {

View File

@@ -118,7 +118,6 @@ func runAiNlToolGetTest(t *testing.T) {
map[string]any{
"name": "question",
"type": "string",
"required": true,
"description": "The natural language question to ask.",
"authSources": []any{},
},

View File

@@ -147,7 +147,7 @@ func TestAlloyDBPgToolEndpoints(t *testing.T) {
toolsFile := tests.GetToolsConfig(sourceConfig, ALLOYDB_POSTGRES_TOOL_KIND, tool_statement1, tool_statement2)
toolsFile = tests.AddPgExecuteSqlConfig(t, toolsFile)
tmplSelectCombined, tmplSelectFilterCombined := tests.GetPostgresSQLTmplToolStatement()
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, ALLOYDB_POSTGRES_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined, "")
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, ALLOYDB_POSTGRES_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined)
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
@@ -166,11 +166,11 @@ func TestAlloyDBPgToolEndpoints(t *testing.T) {
tests.RunToolGetTest(t)
select1Want, failInvocationWant, createTableStatement := tests.GetPostgresWants()
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, tmplSelectAllWant, tmplSelect1Want := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tmplSelectAllWant, tmplSelect1Want, false)
}
// Test connection with different IP type

View File

@@ -94,12 +94,6 @@ func TestBigQueryToolEndpoints(t *testing.T) {
datasetName,
strings.ReplaceAll(uuid.New().String(), "-", ""),
)
tableNameTemplateParam := fmt.Sprintf("`%s.%s.template_param_table_%s`",
BIGQUERY_PROJECT,
datasetName,
strings.ReplaceAll(uuid.New().String(), "-", ""),
)
// set up data for param tool
create_statement1, insert_statement1, tool_statement1, params1 := getBigQueryParamToolInfo(tableNameParam)
teardownTable1 := setupBigQueryTable(t, ctx, client, create_statement1, insert_statement1, datasetName, tableNameParam, params1)
@@ -113,8 +107,6 @@ func TestBigQueryToolEndpoints(t *testing.T) {
// Write config into a file and pass it to command
toolsFile := tests.GetToolsConfig(sourceConfig, BIGQUERY_TOOL_KIND, tool_statement1, tool_statement2)
toolsFile = addBigQueryPrebuiltToolsConfig(t, toolsFile)
tmplSelectCombined, tmplSelectFilterCombined := getBigQueryTmplToolStatement()
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, BIGQUERY_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined, "")
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
@@ -137,14 +129,9 @@ func TestBigQueryToolEndpoints(t *testing.T) {
failInvocationWant := `{"jsonrpc":"2.0","id":"invoke-fail-tool","result":{"content":[{"type":"text","text":"unable to execute query: googleapi: Error 400: Syntax error: Unexpected identifier \"SELEC\" at [1:1]`
datasetInfoWant := "\"Location\":\"US\",\"DefaultTableExpiration\":0,\"Labels\":null,\"Access\":"
tableInfoWant := "[{\"Name\":\"\",\"Location\":\"US\",\"Description\":\"\",\"Schema\":[{\"Name\":\"id\""
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, _, _ := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
templateParamTestConfig := tests.NewTemplateParameterTestConfig(
tests.WithCreateColArray(`["id INT64", "name STRING", "age INT64"]`),
)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, templateParamTestConfig)
runBigQueryExecuteSqlToolInvokeTest(t, select1Want, invokeParamWant, tableNameParam)
runBigQueryListDatasetToolInvokeTest(t, datasetName)
runBigQueryGetDatasetInfoToolInvokeTest(t, datasetName, datasetInfoWant)
@@ -182,13 +169,6 @@ func getBigQueryAuthToolInfo(tableName string) (string, string, string, []bigque
return createStatement, insertStatement, toolStatement, params
}
// getBigQueryTmplToolStatement returns statements for template parameter test cases for bigquery kind
func getBigQueryTmplToolStatement() (string, string) {
tmplSelectCombined := "SELECT * FROM {{.tableName}} WHERE id = ? ORDER BY id"
tmplSelectFilterCombined := "SELECT * FROM {{.tableName}} WHERE {{.columnFilter}} = ? ORDER BY id"
return tmplSelectCombined, tmplSelectFilterCombined
}
func setupBigQueryTable(t *testing.T, ctx context.Context, client *bigqueryapi.Client, create_statement, insert_statement, datasetName string, tableName string, params []bigqueryapi.QueryParameter) func(*testing.T) {
// Create dataset
dataset := client.Dataset(datasetName)

View File

@@ -29,7 +29,6 @@ import (
"cloud.google.com/go/bigtable"
"github.com/google/uuid"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/tests"
)
@@ -70,7 +69,6 @@ func TestBigtableToolEndpoints(t *testing.T) {
tableName := "param_table" + strings.ReplaceAll(uuid.New().String(), "-", "")
tableNameAuth := "auth_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
tableNameTemplateParam := "tmpl_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
columnFamilyName := "cf"
muts, rowKeys := getTestData(columnFamilyName)
@@ -87,14 +85,8 @@ func TestBigtableToolEndpoints(t *testing.T) {
teardownTable2 := setupBtTable(t, ctx, sourceConfig["project"].(string), sourceConfig["instance"].(string), tableNameAuth, columnFamilyName, muts, rowKeys)
defer teardownTable2(t)
mutsTmpl, rowKeysTmpl := getTestDataTemplateParam(columnFamilyName)
teardownTableTmpl := setupBtTable(t, ctx, sourceConfig["project"].(string), sourceConfig["instance"].(string), tableNameTemplateParam, columnFamilyName, mutsTmpl, rowKeysTmpl)
defer teardownTableTmpl(t)
// Write config into a file and pass it to command
toolsFile := tests.GetToolsConfig(sourceConfig, BIGTABLE_TOOL_KIND, param_test_statement, auth_tool_statement)
toolsFile = addTemplateParamConfig(t, toolsFile)
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
t.Fatalf("command initialization returned an error: %s", err)
@@ -114,25 +106,9 @@ func TestBigtableToolEndpoints(t *testing.T) {
// Actual test parameters are set in https://github.com/googleapis/genai-toolbox/blob/52b09a67cb40ac0c5f461598b4673136699a3089/tests/tool_test.go#L250
select1Want := "[{\"$col1\":1}]"
failInvocationWant := `{"jsonrpc":"2.0","id":"invoke-fail-tool","result":{"content":[{"type":"text","text":"unable to prepare statement: rpc error: code = InvalidArgument desc = Syntax error: Unexpected identifier \"SELEC\" [at 1:1]"}],"isError":true}}`
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, _, _ := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
templateParamTestConfig := tests.NewTemplateParameterTestConfig(
tests.WithIgnoreDdl(),
tests.WithIgnoreInsert(),
tests.WithReplaceNameFieldArray(`["CAST(cf['name'] AS string) as name"]`),
tests.WithReplaceNameColFilter("CAST(cf['name'] AS string)"),
)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, templateParamTestConfig)
}
func convertToBytes(v int) []byte {
binary1 := new(bytes.Buffer)
if err := binary.Write(binary1, binary.BigEndian, int64(v)); err != nil {
log.Fatalf("Unable to encode id: %v", err)
}
return binary1.Bytes()
}
func getTestData(columnFamilyName string) ([]*bigtable.Mutation, []string) {
@@ -141,7 +117,11 @@ func getTestData(columnFamilyName string) ([]*bigtable.Mutation, []string) {
var ids [3][]byte
for i := range ids {
ids[i] = convertToBytes(i + 1)
binary1 := new(bytes.Buffer)
if err := binary.Write(binary1, binary.BigEndian, int64(i+1)); err != nil {
log.Fatalf("Unable to encode id: %v", err)
}
ids[i] = binary1.Bytes()
}
now := bigtable.Time(time.Now())
@@ -174,41 +154,6 @@ func getTestData(columnFamilyName string) ([]*bigtable.Mutation, []string) {
return muts, rowKeys
}
func getTestDataTemplateParam(columnFamilyName string) ([]*bigtable.Mutation, []string) {
muts := []*bigtable.Mutation{}
rowKeys := []string{}
var ids [2][]byte
for i := range ids {
ids[i] = convertToBytes(i + 1)
}
now := bigtable.Time(time.Now())
for rowKey, mutData := range map[string]map[string][]byte{
// Do not change the test data without checking tests/common_test.go.
// The structure and value of seed data has to match https://github.com/googleapis/genai-toolbox/blob/4dba0df12dc438eca3cb476ef52aa17cdf232c12/tests/common_test.go#L200-L251
// Expected values are defined in https://github.com/googleapis/genai-toolbox/blob/52b09a67cb40ac0c5f461598b4673136699a3089/tests/tool_test.go#L229-L310
"row-01": {
"name": []byte("Alex"),
"age": convertToBytes(21),
"id": ids[0],
},
"row-02": {
"name": []byte("Alice"),
"age": convertToBytes(100),
"id": ids[1],
},
} {
mut := bigtable.NewMutation()
for col, v := range mutData {
mut.Set(columnFamilyName, col, now, v)
}
muts = append(muts, mut)
rowKeys = append(rowKeys, rowKey)
}
return muts, rowKeys
}
func setupBtTable(t *testing.T, ctx context.Context, projectId string, instance string, tableName string, columnFamilyName string, muts []*bigtable.Mutation, rowKeys []string) func(*testing.T) {
// Creating clients
adminClient, err := bigtable.NewAdminClient(ctx, projectId, instance)
@@ -268,52 +213,3 @@ func setupBtTable(t *testing.T, ctx context.Context, projectId string, instance
defer adminClient.Close()
}
}
func addTemplateParamConfig(t *testing.T, config map[string]any) map[string]any {
toolsMap, ok := config["tools"].(map[string]any)
if !ok {
t.Fatalf("unable to get tools from config")
}
toolsMap["select-templateParams-tool"] = map[string]any{
"kind": "bigtable-sql",
"source": "my-instance",
"description": "Create table tool with template parameters",
"statement": "SELECT TO_INT64(cf['age']) as age, TO_INT64(cf['id']) as id, CAST(cf['name'] AS string) as name, FROM {{.tableName}};",
"templateParameters": []tools.Parameter{
tools.NewStringParameter("tableName", "some description"),
},
}
toolsMap["select-templateParams-combined-tool"] = map[string]any{
"kind": "bigtable-sql",
"source": "my-instance",
"description": "Create table tool with template parameters",
"statement": "SELECT TO_INT64(cf['age']) as age, TO_INT64(cf['id']) as id, CAST(cf['name'] AS string) as name, FROM {{.tableName}} WHERE TO_INT64(cf['id']) = @id;",
"parameters": []tools.Parameter{tools.NewIntParameter("id", "the id of the user")},
"templateParameters": []tools.Parameter{
tools.NewStringParameter("tableName", "some description"),
},
}
toolsMap["select-fields-templateParams-tool"] = map[string]any{
"kind": "bigtable-sql",
"source": "my-instance",
"description": "Create table tool with template parameters",
"statement": "SELECT {{array .fields}}, FROM {{.tableName}};",
"templateParameters": []tools.Parameter{
tools.NewStringParameter("tableName", "some description"),
tools.NewArrayParameter("fields", "The fields to select from", tools.NewStringParameter("field", "A field that will be returned from the query.")),
},
}
toolsMap["select-filter-templateParams-combined-tool"] = map[string]any{
"kind": "bigtable-sql",
"source": "my-instance",
"description": "Create table tool with template parameters",
"statement": "SELECT TO_INT64(cf['age']) as age, TO_INT64(cf['id']) as id, CAST(cf['name'] AS string) as name, FROM {{.tableName}} WHERE {{.columnFilter}} = @name;",
"parameters": []tools.Parameter{tools.NewStringParameter("name", "the name of the user")},
"templateParameters": []tools.Parameter{
tools.NewStringParameter("tableName", "some description"),
tools.NewStringParameter("columnFilter", "some description"),
},
}
config["tools"] = toolsMap
return config
}

View File

@@ -18,7 +18,6 @@ import (
"context"
"database/sql"
"fmt"
"net/url"
"os"
"regexp"
"slices"
@@ -77,13 +76,7 @@ func getCloudSQLMssqlVars(t *testing.T) map[string]any {
// Copied over from cloud_sql_mssql.go
func initCloudSQLMssqlConnection(project, region, instance, ipAddress, ipType, user, pass, dbname string) (*sql.DB, error) {
// Create dsn
query := fmt.Sprintf("database=%s&cloudsql=%s:%s:%s", dbname, project, region, instance)
url := &url.URL{
Scheme: "sqlserver",
User: url.UserPassword(user, pass),
Host: ipAddress,
RawQuery: query,
}
dsn := fmt.Sprintf("sqlserver://%s:%s@%s?database=%s&cloudsql=%s:%s:%s", user, pass, ipAddress, dbname, project, region, instance)
// Get dial options
dialOpts, err := tests.GetCloudSQLDialOpts(ipType)
@@ -102,7 +95,7 @@ func initCloudSQLMssqlConnection(project, region, instance, ipAddress, ipType, u
// Open database connection
db, err := sql.Open(
"cloudsql-sqlserver-driver",
url.String(),
dsn,
)
if err != nil {
return nil, err
@@ -141,7 +134,7 @@ func TestCloudSQLMssqlToolEndpoints(t *testing.T) {
toolsFile := tests.GetToolsConfig(sourceConfig, CLOUD_SQL_MSSQL_TOOL_KIND, tool_statement1, tool_statement2)
toolsFile = tests.AddMssqlExecuteSqlConfig(t, toolsFile)
tmplSelectCombined, tmplSelectFilterCombined := tests.GetMssqlTmplToolStatement()
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, CLOUD_SQL_MSSQL_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined, "")
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, CLOUD_SQL_MSSQL_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined)
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
@@ -160,11 +153,11 @@ func TestCloudSQLMssqlToolEndpoints(t *testing.T) {
tests.RunToolGetTest(t)
select1Want, failInvocationWant, createTableStatement := tests.GetMssqlWants()
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, tmplSelectAllWant, tmplSelect1Want := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tmplSelectAllWant, tmplSelect1Want, false)
}
// Test connection with different IP type

View File

@@ -128,7 +128,7 @@ func TestCloudSQLMysqlToolEndpoints(t *testing.T) {
toolsFile := tests.GetToolsConfig(sourceConfig, CLOUD_SQL_MYSQL_TOOL_KIND, tool_statement1, tool_statement2)
toolsFile = tests.AddMySqlExecuteSqlConfig(t, toolsFile)
tmplSelectCombined, tmplSelectFilterCombined := tests.GetMysqlTmplToolStatement()
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, CLOUD_SQL_MYSQL_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined, "")
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, CLOUD_SQL_MYSQL_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined)
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
@@ -147,11 +147,11 @@ func TestCloudSQLMysqlToolEndpoints(t *testing.T) {
tests.RunToolGetTest(t)
select1Want, failInvocationWant, createTableStatement := tests.GetMysqlWants()
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, tmplSelectAllWant, tmplSelect1Want := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tmplSelectAllWant, tmplSelect1Want, false)
}
// Test connection with different IP type

View File

@@ -132,7 +132,7 @@ func TestCloudSQLPgSimpleToolEndpoints(t *testing.T) {
toolsFile := tests.GetToolsConfig(sourceConfig, CLOUD_SQL_POSTGRES_TOOL_KIND, tool_statement1, tool_statement2)
toolsFile = tests.AddPgExecuteSqlConfig(t, toolsFile)
tmplSelectCombined, tmplSelectFilterCombined := tests.GetPostgresSQLTmplToolStatement()
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, CLOUD_SQL_POSTGRES_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined, "")
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, CLOUD_SQL_POSTGRES_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined)
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
@@ -151,11 +151,11 @@ func TestCloudSQLPgSimpleToolEndpoints(t *testing.T) {
tests.RunToolGetTest(t)
select1Want, failInvocationWant, createTableStatement := tests.GetPostgresWants()
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, tmplSelectAllWant, tmplSelect1Want := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tmplSelectAllWant, tmplSelect1Want, false)
}
// Test connection with different IP type

View File

@@ -129,17 +129,11 @@ func AddPgExecuteSqlConfig(t *testing.T, config map[string]any) map[string]any {
return config
}
func AddTemplateParamConfig(t *testing.T, config map[string]any, toolKind, tmplSelectCombined, tmplSelectFilterCombined string, tmplSelectAll string) map[string]any {
func AddTemplateParamConfig(t *testing.T, config map[string]any, toolKind, tmplSelectCombined, tmplSelectFilterCombined string) map[string]any {
toolsMap, ok := config["tools"].(map[string]any)
if !ok {
t.Fatalf("unable to get tools from config")
}
selectAll := "SELECT * FROM {{.tableName}} ORDER BY id"
if tmplSelectAll != "" {
selectAll = tmplSelectAll
}
toolsMap["create-table-templateParams-tool"] = map[string]any{
"kind": toolKind,
"source": "my-instance",
@@ -165,7 +159,7 @@ func AddTemplateParamConfig(t *testing.T, config map[string]any, toolKind, tmplS
"kind": toolKind,
"source": "my-instance",
"description": "Create table tool with template parameters",
"statement": selectAll,
"statement": "SELECT * FROM {{.tableName}}",
"templateParameters": []tools.Parameter{
tools.NewStringParameter("tableName", "some description"),
},
@@ -184,7 +178,7 @@ func AddTemplateParamConfig(t *testing.T, config map[string]any, toolKind, tmplS
"kind": toolKind,
"source": "my-instance",
"description": "Create table tool with template parameters",
"statement": "SELECT {{array .fields}} FROM {{.tableName}} ORDER BY id",
"statement": "SELECT {{array .fields}} FROM {{.tableName}}",
"templateParameters": []tools.Parameter{
tools.NewStringParameter("tableName", "some description"),
tools.NewArrayParameter("fields", "The fields to select from", tools.NewStringParameter("field", "A field that will be returned from the query.")),
@@ -335,10 +329,12 @@ func GetMysqlTmplToolStatement() (string, string) {
return tmplSelectCombined, tmplSelectFilterCombined
}
func GetNonSpannerInvokeParamWant() (string, string) {
func GetNonSpannerInvokeParamWant() (string, string, string, string) {
invokeParamWant := "[{\"id\":1,\"name\":\"Alice\"},{\"id\":3,\"name\":\"Sid\"}]"
mcpInvokeParamWant := `{"jsonrpc":"2.0","id":"my-param-tool","result":{"content":[{"type":"text","text":"{\"id\":1,\"name\":\"Alice\"}"},{"type":"text","text":"{\"id\":3,\"name\":\"Sid\"}"}]}}`
return invokeParamWant, mcpInvokeParamWant
tmplSelectAllWant := "[{\"age\":21,\"id\":1,\"name\":\"Alex\"},{\"age\":100,\"id\":2,\"name\":\"Alice\"}]"
tmplSelect1Want := "[{\"age\":21,\"id\":1,\"name\":\"Alex\"}]"
return invokeParamWant, mcpInvokeParamWant, tmplSelectAllWant, tmplSelect1Want
}
// GetPostgresWants return the expected wants for postgres

View File

@@ -100,7 +100,6 @@ func TestCouchbaseToolEndpoints(t *testing.T) {
// Create collection names with UUID
collectionNameParam := "param_" + strings.ReplaceAll(uuid.New().String(), "-", "")
collectionNameAuth := "auth_" + strings.ReplaceAll(uuid.New().String(), "-", "")
collectionNameTemplateParam := "template_param_" + strings.ReplaceAll(uuid.New().String(), "-", "")
// Set up data for param tool
paramToolStatement, params1 := getCouchbaseParamToolInfo(collectionNameParam)
@@ -112,14 +111,8 @@ func TestCouchbaseToolEndpoints(t *testing.T) {
teardownCollection2 := setupCouchbaseCollection(t, ctx, cluster, couchbaseBucket, couchbaseScope, collectionNameAuth, params2)
defer teardownCollection2(t)
// Setup up table for template param tool
tmplSelectCombined, tmplSelectFilterCombined, tmplSelectAll, params3 := getCouchbaseTemplateParamToolInfo()
teardownCollection3 := setupCouchbaseCollection(t, ctx, cluster, couchbaseBucket, couchbaseScope, collectionNameTemplateParam, params3)
defer teardownCollection3(t)
// Write config into a file and pass it to command
toolsFile := tests.GetToolsConfig(sourceConfig, couchbaseToolKind, paramToolStatement, authToolStatement)
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, couchbaseToolKind, tmplSelectCombined, tmplSelectFilterCombined, tmplSelectAll)
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
@@ -140,17 +133,9 @@ func TestCouchbaseToolEndpoints(t *testing.T) {
select1Want := "[{\"$1\":1}]"
failMcpInvocationWant := "{\"jsonrpc\":\"2.0\",\"id\":\"invoke-fail-tool\",\"result\":{\"content\":[{\"type\":\"text\",\"text\":\"unable to execute query: parsing failure | {\\\"statement\\\":\\\"SELEC 1;\\\""
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, _, _ := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failMcpInvocationWant)
templateParamTestConfig := tests.NewTemplateParameterTestConfig(
tests.WithIgnoreDdl(),
tests.WithIgnoreInsert(),
tests.WithSelect1Want("[{\"age\":21,\"id\":1,\"name\":\"Alex\"}]"),
tests.WithSelectAllWant("[{\"age\":21,\"id\":1,\"name\":\"Alex\"},{\"age\":100,\"id\":2,\"name\":\"Alice\"}]"),
)
tests.RunToolInvokeWithTemplateParameters(t, collectionNameTemplateParam, templateParamTestConfig)
}
// setupCouchbaseCollection creates a scope and collection and inserts test data
@@ -255,15 +240,3 @@ func getCouchbaseAuthToolInfo(collectionName string) (string, []map[string]any)
}
return toolStatement, params
}
func getCouchbaseTemplateParamToolInfo() (string, string, string, []map[string]any) {
tmplSelectCombined := "SELECT {{.tableName}}.* FROM {{.tableName}} WHERE id = $id"
tmplSelectFilterCombined := "SELECT {{.tableName}}.* FROM {{.tableName}} WHERE {{.columnFilter}} = $name"
tmplSelectAll := "SELECT {{.tableName}}.* FROM {{.tableName}}"
params := []map[string]any{
{"name": "Alex", "id": 1, "age": 21},
{"name": "Alice", "id": 2, "age": 100},
}
return tmplSelectCombined, tmplSelectFilterCombined, tmplSelectAll, params
}

View File

@@ -261,7 +261,7 @@ func TestHttpToolEndpoints(t *testing.T) {
}
select1Want := `["Hello","World"]`
invokeParamWant, _ := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, _, _, _ := tests.GetNonSpannerInvokeParamWant()
tests.RunToolGetTest(t)
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
runAdvancedHTTPInvokeTest(t)
@@ -282,7 +282,7 @@ func runAdvancedHTTPInvokeTest(t *testing.T) {
name: "invoke my-advanced-tool",
api: "http://127.0.0.1:5000/api/tool/my-advanced-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"animalArray": ["rabbit", "ostrich", "whale"], "id": 3, "path": "tool3", "country": "US", "X-Other-Header": "test"}`)),
requestBody: bytes.NewBuffer([]byte(`{"animalArray": ["rabbit", "ostrich", "whale"], "id": 3, "country": "US", "X-Other-Header": "test"}`)),
want: `["Hello","World"]`,
isErr: false,
},
@@ -290,7 +290,7 @@ func runAdvancedHTTPInvokeTest(t *testing.T) {
name: "invoke my-advanced-tool with wrong params",
api: "http://127.0.0.1:5000/api/tool/my-advanced-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"animalArray": ["rabbit", "ostrich", "whale"], "id": 4, "path": "tool3", "country": "US", "X-Other-Header": "test"}`)),
requestBody: bytes.NewBuffer([]byte(`{"animalArray": ["rabbit", "ostrich", "whale"], "id": 4, "country": "US", "X-Other-Header": "test"}`)),
isErr: true,
},
}
@@ -408,16 +408,11 @@ func getHTTPToolsConfig(sourceConfig map[string]any, toolKind string) map[string
"kind": toolKind,
"source": "other-instance",
"method": "get",
"path": "/{{.path}}?id=2",
"path": "/tool3?id=2",
"description": "some description",
"headers": map[string]string{
"X-Custom-Header": "example",
},
"pathParams": []tools.Parameter{
&tools.StringParameter{
CommonParameter: tools.CommonParameter{Name: "path", Type: "string", Desc: "path param"},
},
},
"queryParams": []tools.Parameter{
tools.NewIntParameter("id", "user ID"), tools.NewStringParameter("country", "country")},
"requestBody": `{

View File

@@ -18,7 +18,6 @@ import (
"context"
"database/sql"
"fmt"
"net/url"
"os"
"regexp"
"strings"
@@ -66,17 +65,10 @@ func getMsSQLVars(t *testing.T) map[string]any {
// Copied over from mssql.go
func initMssqlConnection(host, port, user, pass, dbname string) (*sql.DB, error) {
// Create dsn
query := url.Values{}
query.Add("database", dbname)
url := &url.URL{
Scheme: "sqlserver",
User: url.UserPassword(user, pass),
Host: fmt.Sprintf("%s:%s", host, port),
RawQuery: query.Encode(),
}
dsn := fmt.Sprintf("sqlserver://%s:%s@%s:%s?database=%s", user, pass, host, port, dbname)
// Open database connection
db, err := sql.Open("sqlserver", url.String())
db, err := sql.Open("sqlserver", dsn)
if err != nil {
return nil, fmt.Errorf("sql.Open: %w", err)
}
@@ -114,7 +106,7 @@ func TestMssqlToolEndpoints(t *testing.T) {
toolsFile := tests.GetToolsConfig(sourceConfig, MSSQL_TOOL_KIND, tool_statement1, tool_statement2)
toolsFile = tests.AddMssqlExecuteSqlConfig(t, toolsFile)
tmplSelectCombined, tmplSelectFilterCombined := tests.GetMssqlTmplToolStatement()
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, MSSQL_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined, "")
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, MSSQL_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined)
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
@@ -133,9 +125,9 @@ func TestMssqlToolEndpoints(t *testing.T) {
tests.RunToolGetTest(t)
select1Want, failInvocationWant, createTableStatement := tests.GetMssqlWants()
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, tmplSelectAllWant, tmplSelect1Want := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tmplSelectAllWant, tmplSelect1Want, false)
}

View File

@@ -105,7 +105,7 @@ func TestMysqlToolEndpoints(t *testing.T) {
toolsFile := tests.GetToolsConfig(sourceConfig, MYSQL_TOOL_KIND, tool_statement1, tool_statement2)
toolsFile = tests.AddMySqlExecuteSqlConfig(t, toolsFile)
tmplSelectCombined, tmplSelectFilterCombined := tests.GetMysqlTmplToolStatement()
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, MYSQL_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined, "")
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, MYSQL_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined)
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
@@ -124,9 +124,9 @@ func TestMysqlToolEndpoints(t *testing.T) {
tests.RunToolGetTest(t)
select1Want, failInvocationWant, createTableStatement := tests.GetMysqlWants()
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, tmplSelectAllWant, tmplSelect1Want := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tmplSelectAllWant, tmplSelect1Want, false)
}

View File

@@ -17,7 +17,6 @@ package postgres
import (
"context"
"fmt"
"net/url"
"os"
"regexp"
"strings"
@@ -66,13 +65,8 @@ func getPostgresVars(t *testing.T) map[string]any {
// Copied over from postgres.go
func initPostgresConnectionPool(host, port, user, pass, dbname string) (*pgxpool.Pool, error) {
// urlExample := "postgres:dd//username:password@localhost:5432/database_name"
url := &url.URL{
Scheme: "postgres",
User: url.UserPassword(user, pass),
Host: fmt.Sprintf("%s:%s", host, port),
Path: dbname,
}
pool, err := pgxpool.New(context.Background(), url.String())
i := fmt.Sprintf("postgres://%s:%s@%s:%s/%s", user, pass, host, port, dbname)
pool, err := pgxpool.New(context.Background(), i)
if err != nil {
return nil, fmt.Errorf("Unable to create connection pool: %w", err)
}
@@ -111,7 +105,7 @@ func TestPostgres(t *testing.T) {
toolsFile := tests.GetToolsConfig(sourceConfig, POSTGRES_TOOL_KIND, tool_statement1, tool_statement2)
toolsFile = tests.AddPgExecuteSqlConfig(t, toolsFile)
tmplSelectCombined, tmplSelectFilterCombined := tests.GetPostgresSQLTmplToolStatement()
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, POSTGRES_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined, "")
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, POSTGRES_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined)
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
@@ -130,9 +124,9 @@ func TestPostgres(t *testing.T) {
tests.RunToolGetTest(t)
select1Want, failInvocationWant, createTableStatement := tests.GetPostgresWants()
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, tmplSelectAllWant, tmplSelect1Want := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tmplSelectAllWant, tmplSelect1Want, false)
}

View File

@@ -154,18 +154,14 @@ func TestSpannerToolEndpoints(t *testing.T) {
invokeParamWant := "[{\"id\":\"1\",\"name\":\"Alice\"},{\"id\":\"3\",\"name\":\"Sid\"}]"
mcpInvokeParamWant := `{"jsonrpc":"2.0","id":"my-param-tool","result":{"content":[{"type":"text","text":"{\"id\":\"1\",\"name\":\"Alice\"}"},{"type":"text","text":"{\"id\":\"3\",\"name\":\"Sid\"}"}]}}`
failInvocationWant := `"jsonrpc":"2.0","id":"invoke-fail-tool","result":{"content":[{"type":"text","text":"unable to execute client: unable to parse row: spanner: code = \"InvalidArgument\", desc = \"Syntax error: Unexpected identifier \\\\\\\"SELEC\\\\\\\" [at 1:1]\\\\nSELEC 1;\\\\n^\"`
tmplSelectAllWant := "[{\"age\":\"21\",\"id\":\"1\",\"name\":\"Alex\"},{\"age\":\"100\",\"id\":\"2\",\"name\":\"Alice\"}]"
tmplSelect1Want := "[{\"age\":\"21\",\"id\":\"1\",\"name\":\"Alex\"}]"
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
runSpannerSchemaToolInvokeTest(t, accessSchemaWant)
runSpannerExecuteSqlToolInvokeTest(t, select1Want, invokeParamWant, tableNameParam, tableNameAuth)
templateParamTestConfig := tests.NewTemplateParameterTestConfig(
tests.WithIgnoreDdl(),
tests.WithSelectAllWant("[{\"age\":\"21\",\"id\":\"1\",\"name\":\"Alex\"},{\"age\":\"100\",\"id\":\"2\",\"name\":\"Alice\"}]"),
tests.WithSelect1Want("[{\"age\":\"21\",\"id\":\"1\",\"name\":\"Alex\"}]"),
)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, templateParamTestConfig)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tmplSelectAllWant, tmplSelect1Want, true)
}
// getSpannerToolInfo returns statements and param for my-param-tool for spanner-sql kind

View File

@@ -133,7 +133,7 @@ func TestSQLiteToolEndpoint(t *testing.T) {
// Write config into a file and pass it to command
toolsFile := tests.GetToolsConfig(sourceConfig, SQLITE_TOOL_KIND, tool_statement1, tool_statement2)
tmplSelectCombined, tmplSelectFilterCombined := getSQLiteTmplToolStatement()
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, SQLITE_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined, "")
toolsFile = tests.AddTemplateParamConfig(t, toolsFile, SQLITE_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined)
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
@@ -153,8 +153,8 @@ func TestSQLiteToolEndpoint(t *testing.T) {
select1Want := "[{\"1\":1}]"
failInvocationWant := `{"jsonrpc":"2.0","id":"invoke-fail-tool","result":{"content":[{"type":"text","text":"unable to execute query: SQL logic error: near \"SELEC\": syntax error (1)"}],"isError":true}}`
invokeParamWant, mcpInvokeParamWant := tests.GetNonSpannerInvokeParamWant()
invokeParamWant, mcpInvokeParamWant, tmplSelectAllWant, tmplSelect1Want := tests.GetNonSpannerInvokeParamWant()
tests.RunToolInvokeTest(t, select1Want, invokeParamWant)
tests.RunMCPToolCallMethod(t, mcpInvokeParamWant, failInvocationWant)
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tests.NewTemplateParameterTestConfig())
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam, tmplSelectAllWant, tmplSelect1Want, false)
}

View File

@@ -211,97 +211,13 @@ func RunToolInvokeTest(t *testing.T, select1Want, invokeParamWant string) {
}
}
// TemplateParameterTestConfig represents the various configuration options for template parameter tests.
type TemplateParameterTestConfig struct {
ignoreDdl bool
ignoreInsert bool
selectAllWant string
select1Want string
nameFieldArray string
nameColFilter string
createColArray string
}
type Option func(*TemplateParameterTestConfig)
// WithIgnoreDdl is the option function to configure ignoreDdl.
func WithIgnoreDdl() Option {
return func(c *TemplateParameterTestConfig) {
c.ignoreDdl = true
}
}
// WithIgnoreInsert is the option function to configure ignoreInsert.
func WithIgnoreInsert() Option {
return func(c *TemplateParameterTestConfig) {
c.ignoreInsert = true
}
}
// WithSelectAllWant is the option function to configure selectAllWant.
func WithSelectAllWant(s string) Option {
return func(c *TemplateParameterTestConfig) {
c.selectAllWant = s
}
}
// WithSelect1Want is the option function to configure select1Want.
func WithSelect1Want(s string) Option {
return func(c *TemplateParameterTestConfig) {
c.select1Want = s
}
}
// WithReplaceNameFieldArray is the option function to configure replaceNameFieldArray.
func WithReplaceNameFieldArray(s string) Option {
return func(c *TemplateParameterTestConfig) {
c.nameFieldArray = s
}
}
// WithReplaceNameColFilter is the option function to configure replaceNameColFilter.
func WithReplaceNameColFilter(s string) Option {
return func(c *TemplateParameterTestConfig) {
c.nameColFilter = s
}
}
// WithCreateColArray is the option function to configure replaceNameColFilter.
func WithCreateColArray(s string) Option {
return func(c *TemplateParameterTestConfig) {
c.createColArray = s
}
}
// NewTemplateParameterTestConfig creates a new TemplateParameterTestConfig instances with options.
func NewTemplateParameterTestConfig(options ...Option) *TemplateParameterTestConfig {
templateParamTestOption := &TemplateParameterTestConfig{
ignoreDdl: false,
ignoreInsert: false,
selectAllWant: "[{\"age\":21,\"id\":1,\"name\":\"Alex\"},{\"age\":100,\"id\":2,\"name\":\"Alice\"}]",
select1Want: "[{\"age\":21,\"id\":1,\"name\":\"Alex\"}]",
nameFieldArray: `["name"]`,
nameColFilter: "name",
createColArray: `["id INT","name VARCHAR(20)","age INT"]`,
}
// Apply provided options
for _, option := range options {
option(templateParamTestOption)
}
return templateParamTestOption
}
// RunToolInvokeWithTemplateParameters runs tool invoke test cases with template parameters.
func RunToolInvokeWithTemplateParameters(t *testing.T, tableName string, config *TemplateParameterTestConfig) {
selectOnlyNamesWant := "[{\"name\":\"Alex\"},{\"name\":\"Alice\"}]"
func RunToolInvokeWithTemplateParameters(t *testing.T, tableName, select_all_want, select_only_1_want string, ignoreDdl bool) {
select_only_names_want := "[{\"name\":\"Alex\"},{\"name\":\"Alice\"}]"
// Test tool invoke endpoint
invokeTcs := []struct {
name string
ddl bool
insert bool
api string
requestHeader map[string]string
requestBody io.Reader
@@ -313,13 +229,12 @@ func RunToolInvokeWithTemplateParameters(t *testing.T, tableName string, config
ddl: true,
api: "http://127.0.0.1:5000/api/tool/create-table-templateParams-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"tableName": "%s", "columns":%s}`, tableName, config.createColArray))),
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"tableName": "%s", "columns":["id INT","name VARCHAR(20)","age INT"]}`, tableName))),
want: "null",
isErr: false,
},
{
name: "invoke insert-table-templateParams-tool",
insert: true,
api: "http://127.0.0.1:5000/api/tool/insert-table-templateParams-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"tableName": "%s", "columns":["id","name","age"], "values":"1, 'Alex', 21"}`, tableName))),
@@ -328,7 +243,6 @@ func RunToolInvokeWithTemplateParameters(t *testing.T, tableName string, config
},
{
name: "invoke insert-table-templateParams-tool",
insert: true,
api: "http://127.0.0.1:5000/api/tool/insert-table-templateParams-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"tableName": "%s", "columns":["id","name","age"], "values":"2, 'Alice', 100"}`, tableName))),
@@ -340,7 +254,7 @@ func RunToolInvokeWithTemplateParameters(t *testing.T, tableName string, config
api: "http://127.0.0.1:5000/api/tool/select-templateParams-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"tableName": "%s"}`, tableName))),
want: config.selectAllWant,
want: select_all_want,
isErr: false,
},
{
@@ -348,23 +262,23 @@ func RunToolInvokeWithTemplateParameters(t *testing.T, tableName string, config
api: "http://127.0.0.1:5000/api/tool/select-templateParams-combined-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"id": 1, "tableName": "%s"}`, tableName))),
want: config.select1Want,
want: select_only_1_want,
isErr: false,
},
{
name: "invoke select-fields-templateParams-tool",
api: "http://127.0.0.1:5000/api/tool/select-fields-templateParams-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"tableName": "%s", "fields":%s}`, tableName, config.nameFieldArray))),
want: selectOnlyNamesWant,
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"tableName": "%s", "fields":["name"]}`, tableName))),
want: select_only_names_want,
isErr: false,
},
{
name: "invoke select-filter-templateParams-combined-tool",
api: "http://127.0.0.1:5000/api/tool/select-filter-templateParams-combined-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"name": "Alex", "tableName": "%s", "columnFilter": "%s"}`, tableName, config.nameColFilter))),
want: config.select1Want,
requestBody: bytes.NewBuffer([]byte(fmt.Sprintf(`{"name": "Alex", "tableName": "%s", "columnFilter": "name"}`, tableName))),
want: select_only_1_want,
isErr: false,
},
{
@@ -379,11 +293,7 @@ func RunToolInvokeWithTemplateParameters(t *testing.T, tableName string, config
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
// if test case is DDL and source does not ignore ddl test cases
ddlAllow := !tc.ddl || (tc.ddl && !config.ignoreDdl)
// if test case is insert statement and source does not ignore insert test cases
insertAllow := !tc.insert || (tc.insert && !config.ignoreInsert)
if ddlAllow && insertAllow {
if !tc.ddl || (tc.ddl && !ignoreDdl) {
// Send Tool invocation request
req, err := http.NewRequest(http.MethodPost, tc.api, tc.requestBody)
if err != nil {
@@ -393,7 +303,6 @@ func RunToolInvokeWithTemplateParameters(t *testing.T, tableName string, config
for k, v := range tc.requestHeader {
req.Header.Add(k, v)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("unable to send request: %s", err)