Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion .ci/generate_release_table.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,36 @@ fi


FILES=("linux.amd64" "darwin.arm64" "darwin.amd64" "windows.amd64" "windows.arm64")

echo "Waiting for Kokoro to sign and upload binaries to gs://mcp-toolbox-for-databases..."

# We wait for each of the main binaries
for file_key in "${FILES[@]}"; do
OS=$(echo "$file_key" | cut -d '.' -f 1)
ARCH=$(echo "$file_key" | cut -d '.' -f 2)

if [ "$OS" = 'windows' ]; then
SIGNED_URL="gs://mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox.exe"
else
SIGNED_URL="gs://mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox"
fi

until gsutil stat "${SIGNED_URL}" > /dev/null 2>&1; do
echo "Waiting for signed binary: ${SIGNED_URL}..."
sleep 30
done
echo "Found signed binary: ${SIGNED_URL}!"
done

# Wait for the Linux GPG signature
LINUX_SIG_URL="gs://mcp-toolbox-for-databases/$VERSION/linux/amd64/toolbox.sig"
until gsutil stat "${LINUX_SIG_URL}" > /dev/null 2>&1; do
echo "Waiting for Linux GPG signature: ${LINUX_SIG_URL}..."
sleep 30
done
echo "Found Linux GPG signature: ${LINUX_SIG_URL}!"
Comment on lines +15 to +39

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent the CI build from hanging indefinitely if Kokoro fails or takes too long to sign the binaries, add a maximum attempt limit (timeout) to the polling loops.

# We wait for each of the main binaries
for file_key in "${FILES[@]}"; do
    OS=$(echo "$file_key" | cut -d '.' -f 1)
    ARCH=$(echo "$file_key" | cut -d '.' -f 2)

    if [ "$OS" = 'windows' ]; then
        SIGNED_URL="gs://mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox.exe"
    else
        SIGNED_URL="gs://mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox"
    fi

    attempts=0
    max_attempts=40 # 20 minutes
    until gsutil stat "${SIGNED_URL}" > /dev/null 2>&1; do
        if [ $attempts -ge $max_attempts ]; then
            echo "Error: Timeout waiting for signed binary: ${SIGNED_URL}" >&2
            exit 1
        fi
        echo "Waiting for signed binary: ${SIGNED_URL}..."
        sleep 30
        attempts=$((attempts + 1))
    done
    echo "Found signed binary: ${SIGNED_URL}!"
done

# Wait for the Linux GPG signature
LINUX_SIG_URL="gs://mcp-toolbox-for-databases/$VERSION/linux/amd64/toolbox.sig"
attempts=0
until gsutil stat "${LINUX_SIG_URL}" > /dev/null 2>&1; do
    if [ $attempts -ge $max_attempts ]; then
        echo "Error: Timeout waiting for Linux GPG signature: ${LINUX_SIG_URL}" >&2
        exit 1
    fi
    echo "Waiting for Linux GPG signature: ${LINUX_SIG_URL}..."
    sleep 30
    attempts=$((attempts + 1))
done
echo "Found Linux GPG signature: ${LINUX_SIG_URL}!"



output_string=""

# Define the descriptions - ensure this array's order matches FILES
Expand Down Expand Up @@ -50,7 +80,12 @@ do
SHA256=$(shasum -a 256 toolbox | awk '{print $1}')

# Write the table row
output_string+=$(printf "$ROW_FMT" "[$OS/$ARCH]($URL)" "$description_text" "$SHA256")$'\n'
if [ "$OS" = 'linux' ]; then
SIG_URL="https://storage.googleapis.com/mcp-toolbox-for-databases/$VERSION/linux/amd64/toolbox.sig"
output_string+=$(printf "$ROW_FMT" "[$OS/$ARCH]($URL) ([Signature]($SIG_URL))" "$description_text" "$SHA256")$'\n'
else
output_string+=$(printf "$ROW_FMT" "[$OS/$ARCH]($URL)" "$description_text" "$SHA256")$'\n'
fi

rm toolbox
done
Expand Down
101 changes: 86 additions & 15 deletions .ci/versioned.release.cloudbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ steps:
script: |
#!/usr/bin/env bash
export VERSION=v$(cat ./cmd/version.txt)
gcloud storage cp toolbox.linux.amd64 gs://$_BUCKET_NAME/$VERSION/linux/amd64/toolbox
gcloud storage cp toolbox.linux.amd64 gs://$_UNSIGNED_BUCKET_NAME/$VERSION/linux/amd64/toolbox

- id: "build-linux-amd64-geminicli"
name: golang:1
Expand Down Expand Up @@ -174,7 +174,7 @@ steps:
script: |
#!/usr/bin/env bash
export VERSION=v$(cat ./cmd/version.txt)
gcloud storage cp toolbox.darwin.arm64 gs://$_BUCKET_NAME/$VERSION/darwin/arm64/toolbox
gcloud storage cp toolbox.darwin.arm64 gs://$_UNSIGNED_BUCKET_NAME/$VERSION/darwin/arm64/toolbox

- id: "build-darwin-arm64-geminicli"
name: golang:1
Expand Down Expand Up @@ -248,7 +248,7 @@ steps:
script: |
#!/usr/bin/env bash
export VERSION=v$(cat ./cmd/version.txt)
gcloud storage cp toolbox.darwin.amd64 gs://$_BUCKET_NAME/$VERSION/darwin/amd64/toolbox
gcloud storage cp toolbox.darwin.amd64 gs://$_UNSIGNED_BUCKET_NAME/$VERSION/darwin/amd64/toolbox

- id: "build-darwin-amd64-geminicli"
name: golang:1
Expand Down Expand Up @@ -315,7 +315,7 @@ steps:
script: |
#!/usr/bin/env bash
export VERSION=v$(cat ./cmd/version.txt)
gcloud storage cp toolbox.windows.amd64 gs://$_BUCKET_NAME/$VERSION/windows/amd64/toolbox.exe
gcloud storage cp toolbox.windows.amd64 gs://$_UNSIGNED_BUCKET_NAME/$VERSION/windows/amd64/toolbox.exe

- id: "build-windows-amd64-geminicli"
name: golang:1
Expand Down Expand Up @@ -375,7 +375,7 @@ steps:
script: |
#!/usr/bin/env bash
export VERSION=v$(cat ./cmd/version.txt)
gcloud storage cp toolbox.windows.arm64 gs://$_BUCKET_NAME/$VERSION/windows/arm64/toolbox.exe
gcloud storage cp toolbox.windows.arm64 gs://$_UNSIGNED_BUCKET_NAME/$VERSION/windows/arm64/toolbox.exe

- id: "build-windows-arm64-geminicli"
name: golang:1
Expand Down Expand Up @@ -407,14 +407,88 @@ steps:
export VERSION=v$(cat ./cmd/version.txt)
gcloud storage cp toolbox.geminicli.windows.arm64 gs://$_BUCKET_NAME/geminicli/$VERSION/windows/arm64/toolbox.exe

- id: "trigger-linux-signing"
name: "gcr.io/$PROJECT_ID/stubby-caller"
waitFor:
- "store-linux-amd64"
script: |
#!/usr/bin/env bash
export VERSION=v$(cat ./cmd/version.txt)
stubby call blade:kokoro-api KokoroApi.Build <<EOF
full_job_name: "toolbox/linux/release_sign"
input_file_paths: "/bigstore/$_UNSIGNED_BUCKET_NAME/$VERSION/linux/amd64/toolbox"
EOF

- id: "trigger-macos-signing"
name: "gcr.io/$PROJECT_ID/stubby-caller"
waitFor:
- "store-darwin-arm64"
- "store-darwin-amd64"
script: |
#!/usr/bin/env bash
export VERSION=v$(cat ./cmd/version.txt)
stubby call blade:kokoro-api KokoroApi.Build <<EOF
full_job_name: "toolbox/macos/release_sign"
input_file_paths: "/bigstore/$_UNSIGNED_BUCKET_NAME/$VERSION/darwin/arm64/toolbox"
input_file_paths: "/bigstore/$_UNSIGNED_BUCKET_NAME/$VERSION/darwin/amd64/toolbox"
EOF

- id: "trigger-windows-signing"
name: "gcr.io/$PROJECT_ID/stubby-caller"
waitFor:
- "store-windows-amd64"
- "store-windows-arm64"
script: |
#!/usr/bin/env bash
export VERSION=v$(cat ./cmd/version.txt)
stubby call blade:kokoro-api KokoroApi.Build <<EOF
full_job_name: "toolbox/windows/release_sign"
input_file_paths: "/bigstore/$_UNSIGNED_BUCKET_NAME/$VERSION/windows/amd64/toolbox.exe"
input_file_paths: "/bigstore/$_UNSIGNED_BUCKET_NAME/$VERSION/windows/arm64/toolbox.exe"
EOF

- id: "download-signed-binaries"
name: "gcr.io/cloud-builders/gcloud:latest"
waitFor:
- "trigger-linux-signing"
- "trigger-windows-signing"
- "trigger-macos-signing"
script: |
#!/usr/bin/env bash
set -eo pipefail
export VERSION=v$(cat ./cmd/version.txt)

EXPECTED_FILES=(
"$VERSION/linux/amd64/toolbox"
"$VERSION/linux/amd64/toolbox.sig"
"$VERSION/darwin/arm64/toolbox"
"$VERSION/darwin/amd64/toolbox"
"$VERSION/windows/amd64/toolbox.exe"
"$VERSION/windows/arm64/toolbox.exe"
)

echo "Waiting for Kokoro to sign and upload all binaries to gs://$_BUCKET_NAME..."
for f in "${EXPECTED_FILES[@]}"; do
until gcloud storage stat "gs://$_BUCKET_NAME/$f" >/dev/null 2>&1; do
echo "Waiting for gs://$_BUCKET_NAME/$f..."
sleep 30
done
echo "Found signed file gs://$_BUCKET_NAME/$f!"
done
Comment on lines +470 to +477

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add a timeout limit to the polling loop in the download-signed-binaries step to prevent the Cloud Build pipeline from hanging indefinitely if any of the signing jobs fail.

      echo "Waiting for Kokoro to sign and upload all binaries to gs://$_BUCKET_NAME..."
      for f in "${EXPECTED_FILES[@]}"; do
        attempts=0
        max_attempts=40 # 20 minutes per file
        until gcloud storage stat "gs://$_BUCKET_NAME/$f" >/dev/null 2>&1; do
          if [ $attempts -ge $max_attempts ]; then
            echo "Error: Timeout waiting for gs://$_BUCKET_NAME/$f" >&2
            exit 1
          fi
          echo "Waiting for gs://$_BUCKET_NAME/$f..."
          sleep 30
          attempts=$((attempts + 1))
        done
        echo "Found signed file gs://$_BUCKET_NAME/$f!"
      done


echo "Downloading signed binaries to local workspace..."
gcloud storage cp "gs://$_BUCKET_NAME/$VERSION/linux/amd64/toolbox" toolbox.linux.amd64
gcloud storage cp "gs://$_BUCKET_NAME/$VERSION/darwin/arm64/toolbox" toolbox.darwin.arm64
gcloud storage cp "gs://$_BUCKET_NAME/$VERSION/darwin/amd64/toolbox" toolbox.darwin.amd64
gcloud storage cp "gs://$_BUCKET_NAME/$VERSION/windows/amd64/toolbox.exe" toolbox.windows.amd64
gcloud storage cp "gs://$_BUCKET_NAME/$VERSION/windows/arm64/toolbox.exe" toolbox.windows.arm64

echo "All signed binaries have been successfully downloaded."

- id: "publish-npm-to-ar"
name: node:20
waitFor:
- "build-linux-amd64"
- "build-darwin-arm64"
- "build-darwin-amd64"
- "build-windows-amd64"
- "build-windows-arm64"
- "download-signed-binaries"
script: |
#!/usr/bin/env bash
bash .ci/publish_npm_to_ar.sh
Expand All @@ -432,11 +506,7 @@ steps:
- id: "publish-pypi-to-ar"
name: python:3.12
waitFor:
- "build-linux-amd64"
- "build-darwin-arm64"
- "build-darwin-amd64"
- "build-windows-amd64"
- "build-windows-arm64"
- "download-signed-binaries"
script: |
#!/usr/bin/env bash
bash .ci/publish_pypi_to_ar.sh
Expand Down Expand Up @@ -467,6 +537,7 @@ substitutions:
_AR_HOSTNAME: ${_REGION}-docker.pkg.dev
_AR_REPO_NAME: toolbox
_BUCKET_NAME: mcp-toolbox-for-databases
_UNSIGNED_BUCKET_NAME: mcp-toolbox-for-databases-unsigned
_ASSETS_BUCKET: toolbox-build-assets
_DOCKER_URI: ${_AR_HOSTNAME}/${PROJECT_ID}/${_AR_REPO_NAME}/toolbox
_PUSH_LATEST: "false" # Substituted in trigger
Original file line number Diff line number Diff line change
@@ -1,35 +1,73 @@
<!-- This file has been used in local_quickstart.md, local_quickstart_go.md & local_quickstart_js.md -->
<!-- [START configure_toolbox] -->

In this section, we will download Toolbox, configure our tools in a
`tools.yaml`, and then run the Toolbox server.

1. Download the latest version of Toolbox as a binary:
1. Download the latest version of Toolbox as a binary:

{{< notice tip >}}
Select the
[correct binary](https://github.com/googleapis/mcp-toolbox/releases)
corresponding to your OS and CPU architecture.
Select the
[correct binary](https://github.com/googleapis/mcp-toolbox/releases)
corresponding to your OS and CPU architecture.
{{< /notice >}}
<!-- {x-release-please-start-version} -->
<!-- {x-release-please-start-version} -->

```bash
export OS="linux/amd64" # one of linux/amd64, darwin/arm64, darwin/amd64, windows/amd64, or windows/arm64
curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.5.0/$OS/toolbox
```
Comment on lines 16 to 19

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If the user selects windows/amd64 or windows/arm64 as their OS, the binary on GCS is named toolbox.exe rather than toolbox. Running curl -O .../toolbox will result in a 404 error. Dynamically append the .exe extension when the OS is Windows:

export OS="linux/amd64" # one of linux/amd64, darwin/arm64, darwin/amd64, windows/amd64, or windows/arm64
export EXE="" && [ "${OS%%/*}" = "windows" ] && EXE=".exe"
curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.5.0/$OS/toolbox$EXE

<!-- {x-release-please-end} -->

1. Make the binary executable:
<!-- {x-release-please-end} -->

1. [Optional] Verify the downloaded binary's authenticity and integrity:

We recommend verifying the digital signature of the downloaded binary before running it.

{{< tabpane persist=header >}}
{{< tab header="Linux" lang="bash" >}}

# 1. Download the detached GPG signature file (.sig)

curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.5.0/$OS/toolbox.sig
Comment on lines +30 to +32

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Wrap the GPG signature download URL in release-please markers so that the version is automatically updated in future releases, keeping it in sync with the binary version:

# 1. Download the detached GPG signature file (.sig)

<!-- {x-release-please-start-version} -->

curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.5.0/$OS/toolbox.sig

<!-- {x-release-please-end} -->


# 2. Import Google's public GPG signing key

curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | gpg --import

# 3. Verify the signature against the downloaded binary

gpg --verify toolbox.sig toolbox
{{< /tab >}}
{{< tab header="macOS" lang="bash" >}}

# Verify the code signature

codesign -v --verbose=4 toolbox

{{< /tab >}}
{{< tab header="Windows" lang="powershell" >}}

# Verify the Authenticode digital signature

Get-AuthenticodeSignature .\toolbox.exe | Format-List

{{< /tab >}}
{{< /tabpane >}}

1. Make the binary executable (on Linux and macOS):

```bash
chmod +x toolbox
```

1. Write the following into a `tools.yaml` file. Be sure to update any fields
such as `user`, `password`, or `database` that you may have customized in the
previous step.
1. Write the following into a `tools.yaml` file. Be sure to update any fields
such as `user`, `password`, or `database` that you may have customized in the
previous step.

{{< notice tip >}}
In practice, use environment variable replacement with the format ${ENV_NAME}
instead of hardcoding your secrets into the configuration file.
In practice, use environment variable replacement with the format ${ENV_NAME}
instead of hardcoding your secrets into the configuration file.
{{< /notice >}}

```yaml
Expand Down Expand Up @@ -69,7 +107,7 @@ In this section, we will download Toolbox, configure our tools in a
type: postgres-sql
source: my-pg-source
description: >-
Book a hotel by its ID. If the hotel is successfully booked, returns a NULL, raises an error if not.
Book a hotel by its ID. If the hotel is successfully booked, returns a NULL, raises an error if not.
parameters:
- name: hotel_id
type: string
Expand Down Expand Up @@ -120,14 +158,15 @@ In this section, we will download Toolbox, configure our tools in a

For more info on tools, check out the `Resources` section of the docs.

1. Run the Toolbox server, pointing to the `tools.yaml` file created earlier:
1. Run the Toolbox server, pointing to the `tools.yaml` file created earlier:

```bash
./toolbox --config "tools.yaml"
```
```bash
./toolbox --config "tools.yaml"
```

{{< notice note >}}

{{< notice note >}}
Toolbox enables dynamic reloading by default. To disable, use the
`--disable-reload` flag.
Toolbox enables dynamic reloading by default. To disable, use the
`--disable-reload` flag.
{{< /notice >}}
Comment on lines +161 to 171

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Standardize the indentation of the nested blocks under the list item to 4 spaces. The current 8-space indentation for the code block and notice tags, combined with 4-space indentation for the notice body, is inconsistent and can cause rendering issues in Hugo:

1.  Run the toolbox server, pointing to the `tools.yaml` file created earlier:

    ```bash
    ./toolbox --config "tools.yaml"
    ```

    {{< notice note >}}
    toolbox enables dynamic reloading by default. To disable, use the
    `--disable-reload` flag.
    {{< /notice >}}
References
  1. Tool names must be in snake_case.

<!-- [END configure_toolbox] -->
<!-- [END configure_toolbox] -->
Loading