diff --git a/README.md b/README.md index fe8076d64274..bcc5c9567adb 100644 --- a/README.md +++ b/README.md @@ -446,7 +446,7 @@ After installing a [Gemini CLI extensions](https://geminicli.com/extensions/), t gemini # List extensions -/exttensions list +/extensions list # List MCP servers /mcp list ``` diff --git a/go.mod b/go.mod index bca1c73cc7a6..b4d748c73478 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/neo4j/neo4j-go-driver/v6 v6.0.0 github.com/redis/go-redis/v9 v9.18.0 github.com/sijms/go-ora/v2 v2.9.0 - github.com/snowflakedb/gosnowflake v1.19.1 + github.com/snowflakedb/gosnowflake/v2 v2.0.1 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/testcontainers/testcontainers-go v0.42.0 @@ -117,7 +117,6 @@ require ( github.com/ajg/form v1.5.1 // indirect github.com/apache/arrow-go/v18 v18.4.0 // indirect github.com/apache/arrow/go/v15 v15.0.2 // indirect - github.com/apache/thrift v0.22.0 // indirect github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect github.com/aws/aws-sdk-go-v2/config v1.31.8 // indirect @@ -200,7 +199,6 @@ require ( github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect - github.com/klauspost/asmfmt v1.3.2 // indirect github.com/klauspost/compress v1.18.5 // indirect github.com/klauspost/cpuid/v2 v2.2.11 // indirect github.com/leodido/go-urn v1.4.0 // indirect @@ -209,8 +207,6 @@ require ( github.com/magiconair/properties v1.8.10 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect - github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/go-archive v0.2.0 // indirect github.com/moby/moby/api v1.54.1 // indirect diff --git a/go.sum b/go.sum index c3cf65d34834..a48602468926 100644 --- a/go.sum +++ b/go.sum @@ -547,8 +547,8 @@ github.com/sijms/go-ora/v2 v2.9.0 h1:+iQbUeTeCOFMb5BsOMgUhV8KWyrv9yjKpcK4x7+MFrg github.com/sijms/go-ora/v2 v2.9.0/go.mod h1:QgFInVi3ZWyqAiJwzBQA+nbKYKH77tdp1PYoCqhR2dU= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= -github.com/snowflakedb/gosnowflake v1.19.1 h1:NZMErtdZMu6kooehbONNQmu/W5BPsaX8hYdlBBEHgxs= -github.com/snowflakedb/gosnowflake v1.19.1/go.mod h1:9vGW6LYbUD1UqfjpuNN5a5vtha+u4n1AlsR1BqhHwPA= +github.com/snowflakedb/gosnowflake/v2 v2.0.1 h1:zRHCluGtwFLpc1wzN/T3U2GdWd11dx4/LeLgZBjxHvU= +github.com/snowflakedb/gosnowflake/v2 v2.0.1/go.mod h1:c0hIqJ/dxgaMl7g1o8n4Ca3Mf5YCiiVx9igio/PNqC8= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= diff --git a/internal/sources/snowflake/snowflake.go b/internal/sources/snowflake/snowflake.go index 408bbc1a3759..37979071c3b0 100644 --- a/internal/sources/snowflake/snowflake.go +++ b/internal/sources/snowflake/snowflake.go @@ -21,7 +21,7 @@ import ( "github.com/goccy/go-yaml" "github.com/googleapis/mcp-toolbox/internal/sources" "github.com/jmoiron/sqlx" - _ "github.com/snowflakedb/gosnowflake" + _ "github.com/snowflakedb/gosnowflake/v2" "go.opentelemetry.io/otel/trace" ) diff --git a/internal/tools/bigquery/bigqueryexecutesql/bigqueryexecutesql.go b/internal/tools/bigquery/bigqueryexecutesql/bigqueryexecutesql.go index ac7c27848e4d..3182ca484a9a 100644 --- a/internal/tools/bigquery/bigqueryexecutesql/bigqueryexecutesql.go +++ b/internal/tools/bigquery/bigqueryexecutesql/bigqueryexecutesql.go @@ -192,7 +192,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para dryRunJob, err := bqutil.DryRunQuery(ctx, restService, bqClient.Project(), bqClient.Location, sql, nil, connProps, source.GetMaximumBytesBilled()) if err != nil { - return nil, util.NewClientServerError("query validation failed", http.StatusInternalServerError, err) + return nil, util.ProcessGcpError(err) } statementType := dryRunJob.Statistics.Query.StatementType diff --git a/internal/util/errors_test.go b/internal/util/errors_test.go new file mode 100644 index 000000000000..bec87d200bf2 --- /dev/null +++ b/internal/util/errors_test.go @@ -0,0 +1,64 @@ +// Copyright 2026 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package util + +import ( + "errors" + "net/http" + "testing" + + "google.golang.org/api/googleapi" +) + +func TestProcessGcpErrorTreatsBadRequestAsAgentError(t *testing.T) { + err := ProcessGcpError(&googleapi.Error{ + Code: http.StatusBadRequest, + Message: "Bad Request", + Errors: []googleapi.ErrorItem{{ + Reason: "invalidQuery", + Message: "No matching signature for operator >=", + }}, + }) + + var agentErr *AgentError + if !errors.As(err, &agentErr) { + t.Fatalf("expected AgentError, got %T", err) + } +} + +func TestProcessGcpErrorPreservesAuthFailuresAsServerErrors(t *testing.T) { + tcs := []struct { + name string + code int + }{ + {name: "unauthorized", code: http.StatusUnauthorized}, + {name: "forbidden", code: http.StatusForbidden}, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + err := ProcessGcpError(&googleapi.Error{ + Code: tc.code, + Message: http.StatusText(tc.code), + }) + + var clientServerErr *ClientServerError + if !errors.As(err, &clientServerErr) { + t.Fatalf("expected ClientServerError, got %T", err) + } + if clientServerErr.Code != tc.code { + t.Fatalf("expected code %d, got %d", tc.code, clientServerErr.Code) + } + }) + } +} diff --git a/tests/bigquery/bigquery_integration_test.go b/tests/bigquery/bigquery_integration_test.go index 5b4bf8de34f7..4d5eb140d004 100644 --- a/tests/bigquery/bigquery_integration_test.go +++ b/tests/bigquery/bigquery_integration_test.go @@ -1003,8 +1003,7 @@ func runBigQueryExecuteSqlToolInvokeTest(t *testing.T, select1Want, invokeParamW api: "http://127.0.0.1:5000/api/tool/my-exec-sql-tool/invoke", requestHeader: map[string]string{}, requestBody: bytes.NewBuffer([]byte(`{"sql":"CREATE TABLE t (id SERIAL PRIMARY KEY, name TEXT)"}`)), - want: ddlWant, - isErr: true, + want: `{"error":"error processing GCP request: failed to insert dry run job: googleapi: Error 400: Table \"t\" must be qualified with a dataset (e.g. dataset.table)., invalid"}`, }, { name: "invoke my-exec-sql-tool with data present in table", @@ -1027,8 +1026,7 @@ func runBigQueryExecuteSqlToolInvokeTest(t *testing.T, select1Want, invokeParamW api: "http://127.0.0.1:5000/api/tool/my-exec-sql-tool/invoke", requestHeader: map[string]string{}, requestBody: bytes.NewBuffer([]byte(`{"sql":"DROP TABLE t"}`)), - want: ddlWant, - isErr: true, + want: `{"error":"error processing GCP request: failed to insert dry run job: googleapi: Error 400: Table \"t\" must be qualified with a dataset (e.g. dataset.table)., invalid"}`, }, { name: "invoke my-exec-sql-tool insert entry", diff --git a/tests/snowflake/snowflake_integration_test.go b/tests/snowflake/snowflake_integration_test.go index b7052e793372..41444bf2aeee 100644 --- a/tests/snowflake/snowflake_integration_test.go +++ b/tests/snowflake/snowflake_integration_test.go @@ -27,7 +27,7 @@ import ( "github.com/googleapis/mcp-toolbox/internal/testutils" "github.com/googleapis/mcp-toolbox/tests" "github.com/jmoiron/sqlx" - _ "github.com/snowflakedb/gosnowflake" + _ "github.com/snowflakedb/gosnowflake/v2" ) var (