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
2 changes: 1 addition & 1 deletion cmd/internal/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,7 @@ func TestPrebuiltTools(t *testing.T) {
wantToolset: server.ToolsetConfigs{
"cloud_sql_postgres_admin_tools": tools.ToolsetConfig{
Name: "cloud_sql_postgres_admin_tools",
ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation", "postgres_upgrade_precheck", "clone_instance", "create_backup", "restore_backup"},
ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation", "postgres_upgrade_precheck", "clone_instance", "create_backup", "restore_backup", "execute_sql_many"},
},
},
},
Expand Down
2 changes: 2 additions & 0 deletions cmd/internal/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ import (
_ "github.com/googleapis/mcp-toolbox/internal/tools/oracle/oraclesql"
_ "github.com/googleapis/mcp-toolbox/internal/tools/postgres/postgresdatabaseoverview"
_ "github.com/googleapis/mcp-toolbox/internal/tools/postgres/postgresexecutesql"
_ "github.com/googleapis/mcp-toolbox/internal/tools/postgres/postgresexecutesqlmany"
_ "github.com/googleapis/mcp-toolbox/internal/tools/postgres/postgresgetcolumncardinality"
_ "github.com/googleapis/mcp-toolbox/internal/tools/postgres/postgreslistactivequeries"
_ "github.com/googleapis/mcp-toolbox/internal/tools/postgres/postgreslistavailableextensions"
Expand All @@ -220,6 +221,7 @@ import (
_ "github.com/googleapis/mcp-toolbox/internal/tools/postgres/postgreslongrunningtransactions"
_ "github.com/googleapis/mcp-toolbox/internal/tools/postgres/postgresreplicationstats"
_ "github.com/googleapis/mcp-toolbox/internal/tools/postgres/postgressql"
_ "github.com/googleapis/mcp-toolbox/internal/tools/postgres/postgressqlmany"
_ "github.com/googleapis/mcp-toolbox/internal/tools/redis"
_ "github.com/googleapis/mcp-toolbox/internal/tools/serverlessspark/serverlesssparkcancelbatch"
_ "github.com/googleapis/mcp-toolbox/internal/tools/serverlessspark/serverlesssparkcreatepysparkbatch"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: "postgres-execute-sql-many"
Comment thread
duwenxin99 marked this conversation as resolved.
type: docs
weight: 1
description: >
A "postgres-execute-sql-many" tool executes a SQL statement against a specific Cloud SQL Postgres instance provided at runtime.
---

## About

A `postgres-execute-sql-many` tool executes a SQL statement against a specific Cloud SQL Postgres instance identified by project, instance, and database parameters provided at runtime.

This tool is useful for executing arbitrary SQL queries across multiple database instances without needing to configure a separate tool for each instance.

> **Note:** This tool is intended for developer assistant workflows with human-in-the-loop and shouldn't be used for production agents.

## Compatible Sources

{{< compatible-sources others="integrations/cloud-sql-admin" >}}

## Parameters

The following parameters are required at runtime when invoking the tool:

| **Parameter** | **Type** | **Description** |
| :------------ | :------- | :---------------------------- |
| `project` | string | The GCP project ID. |
| `instance` | string | The Cloud SQL instance ID. |
| `database` | string | The database name. |
| `sql` | string | The SQL statement to execute. |

## Example

```yaml
kind: tool
name: execute_sql_many_tool
type: postgres-execute-sql-many
source: my-cloud-sql-admin-source
description: Use this tool to execute sql statement on a specific instance.
```

## Reference

| **field** | **type** | **required** | **description** |
| :---------- | :------- | :----------- | :------------------------------------------------- |
| type | string | true | Must be "postgres-execute-sql-many". |
| source | string | true | Name of the `cloud-sql-admin` source. |
| description | string | true | Description of the tool that is passed to the LLM. |
56 changes: 56 additions & 0 deletions docs/en/integrations/postgres/tools/postgres-sql-many.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: "postgres-sql-many"
Comment thread
duwenxin99 marked this conversation as resolved.
type: docs
weight: 1
description: >
A "postgres-sql-many" tool executes a predefined SQL statement against a specific Cloud SQL Postgres instance provided at runtime.
---

## About

A `postgres-sql-many` tool executes a predefined SQL statement against a specific Cloud SQL Postgres instance identified by project, instance, and database parameters provided at runtime.

It supports `templateParameters` to allow dynamic values to be injected into the query at runtime.

> **Note:** This tool is intended for developer assistant workflows with human-in-the-loop and shouldn't be used for production agents.

## Compatible Sources

{{< compatible-sources others="integrations/cloud-sql-admin" >}}

## Parameters

The following parameters are required at runtime when invoking the tool:

| **Parameter** | **Type** | **Description** |
| :------------ | :------- | :------------------------- |
| `project` | string | The GCP project ID. |
| `instance` | string | The Cloud SQL instance ID. |
| `database` | string | The database name. |

Additional parameters may be required based on the `templateParameters` configured in the tool definition.

## Example

```yaml
kind: tool
name: get_user_many_tool
type: postgres-sql-many
source: my-cloud-sql-admin-source
description: Use this tool to get user details from a specific instance.
statement: SELECT * FROM users WHERE id = {{.user_id}}
templateParameters:
- name: user_id
type: string
description: The ID of the user.
```

## Reference

| **field** | **type** | **required** | **description** |
| :----------------- | :------- | :----------- | :------------------------------------------------- |
| type | string | true | Must be "postgres-sql-many". |
| source | string | true | Name of the `cloud-sql-admin` source. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | The SQL statement template to execute. |
| templateParameters | list | false | List of parameters used in the statement template. |
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.

remove this change for now as of right now we need more guidnace to use this correctly

Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ tools:
restore_backup:
kind: cloud-sql-restore-backup
source: cloud-sql-admin-source
execute_sql_many:
kind: postgres-execute-sql-many
source: cloud-sql-admin-source

toolsets:
cloud_sql_postgres_admin_tools:
Expand All @@ -66,3 +69,4 @@ toolsets:
- clone_instance
- create_backup
- restore_backup
- execute_sql_many
20 changes: 20 additions & 0 deletions internal/sources/cloudsqladmin/cloud_sql_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ func (s *Source) GetDefaultProject() string {
return s.DefaultProject
}

// GetService returns a new Cloud SQL Admin service for the given access token.

func (s *Source) GetService(ctx context.Context, accessToken string) (*sqladmin.Service, error) {
if s.UseClientOAuth {
token := &oauth2.Token{AccessToken: accessToken}
Expand Down Expand Up @@ -294,6 +296,24 @@ func (s *Source) ListInstance(ctx context.Context, project, accessToken string)
return instances, nil
}

func (s *Source) ExecuteSql(ctx context.Context, project, instance, database, sql string, accessToken string) (any, error) {
service, err := s.GetService(ctx, accessToken)
if err != nil {
return nil, err
}

req := &sqladmin.ExecuteSqlPayload{
Database: database,
SqlStatement: sql,
}

resp, err := service.Instances.ExecuteSql(project, instance, req).Do()
if err != nil {
return nil, fmt.Errorf("error executing sql: %w", err)
}
return resp, nil
}

func (s *Source) CreateInstance(ctx context.Context, project, name, dbVersion, rootPassword string, settings sqladmin.Settings, accessToken string) (any, error) {
instance := sqladmin.DatabaseInstance{
Name: name,
Expand Down
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.

These tools won't be generic postgres so we should move them to a different tool source directory

Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// 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 postgresexecutesqlmany

import (
"context"
"fmt"
"net/http"

yaml "github.com/goccy/go-yaml"
"github.com/googleapis/mcp-toolbox/internal/embeddingmodels"
"github.com/googleapis/mcp-toolbox/internal/sources"
"github.com/googleapis/mcp-toolbox/internal/tools"
"github.com/googleapis/mcp-toolbox/internal/util"
"github.com/googleapis/mcp-toolbox/internal/util/parameters"
)

const resourceType string = "postgres-execute-sql-many"

func init() {
if !tools.Register(resourceType, newConfig) {
panic(fmt.Sprintf("tool type %q already registered", resourceType))
}
}

func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}

type compatibleSource interface {
ExecuteSql(ctx context.Context, project, instance, database, sql string, accessToken string) (any, error)
UseClientAuthorization() bool
}

type Config struct {
Name string `yaml:"name" validate:"required"`
Type string `yaml:"type" validate:"required"`
Source string `yaml:"source" validate:"required"`
Description string `yaml:"description"`
AuthRequired []string `yaml:"authRequired"`
Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"`
}

var _ tools.ToolConfig = Config{}

func (cfg Config) ToolConfigType() string {
return resourceType
}

// Initialize creates a new Postgres ExecuteSqlMany tool.
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
params := parameters.Parameters{
parameters.NewStringParameter("project", "The GCP project ID."),
parameters.NewStringParameter("instance", "The Cloud SQL instance ID."),
parameters.NewStringParameter("database", "The database name."),
parameters.NewStringParameter("sql", "The SQL statement to execute."),
Comment thread
duwenxin99 marked this conversation as resolved.
}

description := cfg.Description
if description == "" {
description = "Executes multiple SQL statements on a Postgres database."
}

annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewDestructiveAnnotations)
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, params, annotations)

t := Tool{
Config: cfg,
Parameters: params,
manifest: tools.Manifest{Description: description, Parameters: params.Manifest(), AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
}
return t, nil
}

var _ tools.Tool = Tool{}

type Tool struct {
Config
Parameters parameters.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
}

// Invoke executes the SQL statement on the given database.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
// Check source compatibility
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}

// Extract parameters from the parameter values map.
paramsMap := params.AsMap()
project, _ := paramsMap["project"].(string)
instance, _ := paramsMap["instance"].(string)
database, _ := paramsMap["database"].(string)
sql, _ := paramsMap["sql"].(string)

logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query on %s/%s/%s", resourceType, project, instance, database))

// Execute the SQL statement on the given database.
resp, err := source.ExecuteSql(ctx, project, instance, database, sql, string(accessToken))
if err != nil {
return nil, util.ProcessGcpError(err)
}
return resp, nil
}

func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.Parameters, paramValues, embeddingModelsMap, nil)
}

func (t Tool) Manifest() tools.Manifest {
return t.manifest
}

func (t Tool) McpManifest() tools.McpManifest {
return t.mcpManifest
}

func (t Tool) Authorized(verifiedAuthServices []string) bool {
return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
}

func (t Tool) RequiresClientAuthorization(resourceMgr tools.SourceProvider) (bool, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return false, err
}
return source.UseClientAuthorization(), nil
}

func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}

func (t Tool) GetAuthTokenHeaderName(resourceMgr tools.SourceProvider) (string, error) {
return "Authorization", nil
}

func (t Tool) GetParameters() parameters.Parameters {
return t.Parameters
}
Loading
Loading