refactor: implement dynamic tool registration (#613)

This PR refactors the tool configuration and loading mechanism to use a
dynamic registration pattern. Each tool package now registers itself
with a central registry, and the server configuration code uses this
registry to decode and initialize tools.

Key changes:
- Introduced tools.Register and tools.DecodeToolConfig for dynamic tool
handling.
- Removed direct imports of specific tool packages from
internal/server/config.go.
- Updated individual tool packages to include init() functions for
self-registration.
- Modified ToolKind constants to be local kind constants within each
tool package.
- Adjusted test files to reflect the changes in tool kind identifiers.

This change simplifies adding new tools and decouples the server
configuration from specific tool implementations.

---------

Co-authored-by: Yuan Teoh <yuanteoh@google.com>
Co-authored-by: Yuan <45984206+Yuan325@users.noreply.github.com>
This commit is contained in:
Kurtis Van Gent
2025-06-04 11:19:42 -06:00
committed by GitHub
parent f5de1af5bd
commit b4862825e8
47 changed files with 506 additions and 270 deletions

View File

@@ -32,6 +32,30 @@ import (
"github.com/googleapis/genai-toolbox/internal/server"
"github.com/googleapis/genai-toolbox/internal/telemetry"
"github.com/googleapis/genai-toolbox/internal/util"
// Import tool packages for side effect of registration
_ "github.com/googleapis/genai-toolbox/internal/tools/alloydbainl"
_ "github.com/googleapis/genai-toolbox/internal/tools/bigquery"
_ "github.com/googleapis/genai-toolbox/internal/tools/bigqueryexecutesql"
_ "github.com/googleapis/genai-toolbox/internal/tools/bigquerygetdatasetinfo"
_ "github.com/googleapis/genai-toolbox/internal/tools/bigquerygettableinfo"
_ "github.com/googleapis/genai-toolbox/internal/tools/bigquerylistdatasetids"
_ "github.com/googleapis/genai-toolbox/internal/tools/bigquerylisttableids"
_ "github.com/googleapis/genai-toolbox/internal/tools/bigtable"
_ "github.com/googleapis/genai-toolbox/internal/tools/couchbase"
_ "github.com/googleapis/genai-toolbox/internal/tools/dgraph"
_ "github.com/googleapis/genai-toolbox/internal/tools/http"
_ "github.com/googleapis/genai-toolbox/internal/tools/mssqlexecutesql"
_ "github.com/googleapis/genai-toolbox/internal/tools/mssqlsql"
_ "github.com/googleapis/genai-toolbox/internal/tools/mysqlexecutesql"
_ "github.com/googleapis/genai-toolbox/internal/tools/mysqlsql"
_ "github.com/googleapis/genai-toolbox/internal/tools/neo4j"
_ "github.com/googleapis/genai-toolbox/internal/tools/postgresexecutesql"
_ "github.com/googleapis/genai-toolbox/internal/tools/postgressql"
_ "github.com/googleapis/genai-toolbox/internal/tools/spanner"
_ "github.com/googleapis/genai-toolbox/internal/tools/spannerexecutesql"
_ "github.com/googleapis/genai-toolbox/internal/tools/sqlitesql"
"github.com/spf13/cobra"
)

View File

@@ -358,7 +358,7 @@ func TestParseToolFile(t *testing.T) {
Tools: server.ToolConfigs{
"example_tool": postgressql.Config{
Name: "example_tool",
Kind: postgressql.ToolKind,
Kind: "postgres-sql",
Source: "my-pg-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",
@@ -489,7 +489,7 @@ func TestParseToolFileWithAuth(t *testing.T) {
Tools: server.ToolConfigs{
"example_tool": postgressql.Config{
Name: "example_tool",
Kind: postgressql.ToolKind,
Kind: "postgres-sql",
Source: "my-pg-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",
@@ -588,7 +588,7 @@ func TestParseToolFileWithAuth(t *testing.T) {
Tools: server.ToolConfigs{
"example_tool": postgressql.Config{
Name: "example_tool",
Kind: postgressql.ToolKind,
Kind: "postgres-sql",
Source: "my-pg-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",
@@ -689,7 +689,7 @@ func TestParseToolFileWithAuth(t *testing.T) {
Tools: server.ToolConfigs{
"example_tool": postgressql.Config{
Name: "example_tool",
Kind: postgressql.ToolKind,
Kind: "postgres-sql",
Source: "my-pg-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",
@@ -842,7 +842,7 @@ func TestEnvVarReplacement(t *testing.T) {
Tools: server.ToolConfigs{
"example_tool": http.Config{
Name: "example_tool",
Kind: http.ToolKind,
Kind: "http",
Source: "my-instance",
Method: "GET",
Path: "search?name=alice&pet=cat",

View File

@@ -38,27 +38,6 @@ import (
spannersrc "github.com/googleapis/genai-toolbox/internal/sources/spanner"
sqlitesrc "github.com/googleapis/genai-toolbox/internal/sources/sqlite"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/alloydbainl"
"github.com/googleapis/genai-toolbox/internal/tools/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools/bigqueryexecutesql"
"github.com/googleapis/genai-toolbox/internal/tools/bigquerygetdatasetinfo"
"github.com/googleapis/genai-toolbox/internal/tools/bigquerygettableinfo"
"github.com/googleapis/genai-toolbox/internal/tools/bigquerylistdatasetids"
"github.com/googleapis/genai-toolbox/internal/tools/bigquerylisttableids"
"github.com/googleapis/genai-toolbox/internal/tools/bigtable"
couchbasetool "github.com/googleapis/genai-toolbox/internal/tools/couchbase"
"github.com/googleapis/genai-toolbox/internal/tools/dgraph"
httptool "github.com/googleapis/genai-toolbox/internal/tools/http"
"github.com/googleapis/genai-toolbox/internal/tools/mssqlexecutesql"
"github.com/googleapis/genai-toolbox/internal/tools/mssqlsql"
"github.com/googleapis/genai-toolbox/internal/tools/mysqlexecutesql"
"github.com/googleapis/genai-toolbox/internal/tools/mysqlsql"
neo4jtool "github.com/googleapis/genai-toolbox/internal/tools/neo4j"
"github.com/googleapis/genai-toolbox/internal/tools/postgresexecutesql"
"github.com/googleapis/genai-toolbox/internal/tools/postgressql"
"github.com/googleapis/genai-toolbox/internal/tools/spanner"
"github.com/googleapis/genai-toolbox/internal/tools/spannerexecutesql"
"github.com/googleapis/genai-toolbox/internal/tools/sqlitesql"
"github.com/googleapis/genai-toolbox/internal/util"
)
@@ -340,146 +319,25 @@ func (c *ToolConfigs) UnmarshalYAML(ctx context.Context, unmarshal func(interfac
v["authRequired"] = []string{}
}
kind, ok := v["kind"]
kindVal, ok := v["kind"]
if !ok {
return fmt.Errorf("missing 'kind' field for %q", name)
return fmt.Errorf("missing 'kind' field for tool %q", name)
}
kindStr, ok := kindVal.(string)
if !ok {
return fmt.Errorf("invalid 'kind' field for tool %q (must be a string)", name)
}
dec, err := util.NewStrictDecoder(v)
yamlDecoder, err := util.NewStrictDecoder(v)
if err != nil {
return fmt.Errorf("error creating decoder: %w", err)
}
switch kind {
case bigtable.ToolKind:
actual := bigtable.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case postgressql.ToolKind:
actual := postgressql.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case alloydbainl.ToolKind:
actual := alloydbainl.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case mysqlsql.ToolKind:
actual := mysqlsql.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case spanner.ToolKind:
actual := spanner.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case neo4jtool.ToolKind:
actual := neo4jtool.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case mssqlsql.ToolKind:
actual := mssqlsql.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case dgraph.ToolKind:
actual := dgraph.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case httptool.ToolKind:
actual := httptool.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case bigquery.ToolKind:
actual := bigquery.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case sqlitesql.ToolKind:
actual := sqlitesql.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case postgresexecutesql.ToolKind:
actual := postgresexecutesql.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case mysqlexecutesql.ToolKind:
actual := mysqlexecutesql.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case spannerexecutesql.ToolKind:
actual := spannerexecutesql.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case mssqlexecutesql.ToolKind:
actual := mssqlexecutesql.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case couchbasetool.ToolKind:
actual := couchbasetool.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case bigqueryexecutesql.ToolKind:
actual := bigqueryexecutesql.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case bigquerylistdatasetids.ToolKind:
actual := bigquerylistdatasetids.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case bigquerygetdatasetinfo.ToolKind:
actual := bigquerygetdatasetinfo.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case bigquerylisttableids.ToolKind:
actual := bigquerylisttableids.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
case bigquerygettableinfo.ToolKind:
actual := bigquerygettableinfo.Config{Name: name}
if err := dec.DecodeContext(ctx, &actual); err != nil {
return fmt.Errorf("unable to parse as %q: %w", kind, err)
}
(*c)[name] = actual
default:
return fmt.Errorf("%q is not a valid kind of tool", kind)
return fmt.Errorf("error creating YAML decoder for tool %q: %w", name, err)
}
toolCfg, err := tools.DecodeConfig(ctx, kindStr, name, yamlDecoder)
if err != nil {
return err
}
(*c)[name] = toolCfg
}
return nil
}

View File

@@ -19,13 +19,28 @@ import (
"fmt"
"strings"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/alloydbpg"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/jackc/pgx/v5/pgxpool"
)
const ToolKind string = "alloydb-ai-nl"
const kind string = "alloydb-ai-nl"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
PostgresPool() *pgxpool.Pool
@@ -50,7 +65,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -63,7 +78,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
numParams := len(cfg.NLConfigParameters)
@@ -112,7 +127,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.NLConfigParameters,
Statement: stmt,
NLConfig: cfg.NLConfig,

View File

@@ -57,7 +57,7 @@ func TestParseFromYamlAlloyDBNLA(t *testing.T) {
want: server.ToolConfigs{
"example_tool": alloydbainl.Config{
Name: "example_tool",
Kind: alloydbainl.ToolKind,
Kind: "alloydb-ai-nl",
Source: "my-alloydb-instance",
Description: "AlloyDB natural language query tool",
NLConfig: "my_nl_config",
@@ -98,7 +98,7 @@ func TestParseFromYamlAlloyDBNLA(t *testing.T) {
want: server.ToolConfigs{
"complex_tool": alloydbainl.Config{
Name: "complex_tool",
Kind: alloydbainl.ToolKind,
Kind: "alloydb-ai-nl",
Source: "my-alloydb-instance",
Description: "AlloyDB natural language query tool with multiple parameters",
NLConfig: "complex_nl_config",

View File

@@ -20,13 +20,28 @@ import (
"strings"
bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools"
"google.golang.org/api/iterator"
)
const ToolKind string = "bigquery-sql"
const kind string = "bigquery-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
BigQueryClient() *bigqueryapi.Client
@@ -51,7 +66,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -64,7 +79,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.McpManifest{
@@ -76,7 +91,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,

View File

@@ -53,7 +53,7 @@ func TestParseFromYamlBigQuery(t *testing.T) {
want: server.ToolConfigs{
"example_tool": bigquery.Config{
Name: "example_tool",
Kind: bigquery.ToolKind,
Kind: "bigquery-sql",
Source: "my-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",

View File

@@ -19,13 +19,28 @@ import (
"fmt"
bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools"
"google.golang.org/api/iterator"
)
const ToolKind string = "bigquery-execute-sql"
const kind string = "bigquery-execute-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
BigQueryClient() *bigqueryapi.Client
@@ -48,7 +63,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -61,7 +76,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
@@ -76,7 +91,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.BigQueryClient(),

View File

@@ -46,7 +46,7 @@ func TestParseFromYamlBigQueryExecuteSql(t *testing.T) {
want: server.ToolConfigs{
"example_tool": bigqueryexecutesql.Config{
Name: "example_tool",
Kind: bigqueryexecutesql.ToolKind,
Kind: "bigquery-execute-sql",
Source: "my-instance",
Description: "some description",
AuthRequired: []string{},

View File

@@ -19,12 +19,27 @@ import (
"fmt"
bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "bigquery-get-dataset-info"
const kind string = "bigquery-get-dataset-info"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
BigQueryClient() *bigqueryapi.Client
@@ -47,7 +62,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -60,7 +75,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
datasetParameter := tools.NewStringParameter("dataset", "The dataset to get metadata information.")
@@ -75,7 +90,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.BigQueryClient(),

View File

@@ -46,7 +46,7 @@ func TestParseFromYamlBigQueryGetDatasetInfo(t *testing.T) {
want: server.ToolConfigs{
"example_tool": bigquerygetdatasetinfo.Config{
Name: "example_tool",
Kind: bigquerygetdatasetinfo.ToolKind,
Kind: "bigquery-get-dataset-info",
Source: "my-instance",
Description: "some description",
AuthRequired: []string{},

View File

@@ -19,12 +19,27 @@ import (
"fmt"
bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "bigquery-get-table-info"
const kind string = "bigquery-get-table-info"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
BigQueryClient() *bigqueryapi.Client
@@ -47,7 +62,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -60,7 +75,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
datasetParameter := tools.NewStringParameter("dataset", "The table's parent dataset.")
@@ -76,7 +91,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.BigQueryClient(),

View File

@@ -46,7 +46,7 @@ func TestParseFromYamlBigQueryGetTableInfo(t *testing.T) {
want: server.ToolConfigs{
"example_tool": bigquerygettableinfo.Config{
Name: "example_tool",
Kind: bigquerygettableinfo.ToolKind,
Kind: "bigquery-get-table-info",
Source: "my-instance",
Description: "some description",
AuthRequired: []string{},

View File

@@ -19,13 +19,28 @@ import (
"fmt"
bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools"
"google.golang.org/api/iterator"
)
const ToolKind string = "bigquery-list-dataset-ids"
const kind string = "bigquery-list-dataset-ids"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
BigQueryClient() *bigqueryapi.Client
@@ -48,7 +63,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -61,7 +76,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
parameters := tools.Parameters{}
@@ -75,7 +90,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.BigQueryClient(),

View File

@@ -46,7 +46,7 @@ func TestParseFromYamlBigQueryListDatasetIds(t *testing.T) {
want: server.ToolConfigs{
"example_tool": bigquerylistdatasetids.Config{
Name: "example_tool",
Kind: bigquerylistdatasetids.ToolKind,
Kind: "bigquery-list-dataset-ids",
Source: "my-instance",
Description: "some description",
AuthRequired: []string{},

View File

@@ -19,13 +19,28 @@ import (
"fmt"
bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools"
"google.golang.org/api/iterator"
)
const ToolKind string = "bigquery-list-table-ids"
const kind string = "bigquery-list-table-ids"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
BigQueryClient() *bigqueryapi.Client
@@ -48,7 +63,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -61,7 +76,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
datasetParameter := tools.NewStringParameter("dataset", "The dataset to list table ids.")
@@ -76,7 +91,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.BigQueryClient(),

View File

@@ -46,7 +46,7 @@ func TestParseFromYamlBigQueryListTableIds(t *testing.T) {
want: server.ToolConfigs{
"example_tool": bigquerylisttableids.Config{
Name: "example_tool",
Kind: bigquerylisttableids.ToolKind,
Kind: "bigquery-list-table-ids",
Source: "my-instance",
Description: "some description",
AuthRequired: []string{},

View File

@@ -19,12 +19,27 @@ import (
"fmt"
"cloud.google.com/go/bigtable"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
bigtabledb "github.com/googleapis/genai-toolbox/internal/sources/bigtable"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "bigtable-sql"
const kind string = "bigtable-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
BigtableClient() *bigtable.Client
@@ -49,7 +64,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -62,7 +77,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.McpManifest{
@@ -74,7 +89,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,

View File

@@ -53,7 +53,7 @@ func TestParseFromYamlBigtable(t *testing.T) {
want: server.ToolConfigs{
"example_tool": bigtable.Config{
Name: "example_tool",
Kind: bigtable.ToolKind,
Kind: "bigtable-sql",
Source: "my-pg-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",

View File

@@ -20,12 +20,27 @@ import (
"fmt"
"github.com/couchbase/gocb/v2"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/couchbase"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "couchbase-sql"
const kind string = "couchbase-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
CouchbaseScope() *gocb.Scope
@@ -51,7 +66,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -64,7 +79,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.McpManifest{
@@ -75,7 +90,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
Scope: s.CouchbaseScope(),

View File

@@ -50,7 +50,7 @@ func TestParseFromYamlCouchbase(t *testing.T) {
want: server.ToolConfigs{
"example_tool": couchbase.Config{
Name: "example_tool",
Kind: couchbase.ToolKind,
Kind: "couchbase-sql",
AuthRequired: []string{},
Source: "my-couchbase-instance",
Description: "some tool description",

View File

@@ -19,12 +19,27 @@ import (
"encoding/json"
"fmt"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/dgraph"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "dgraph-dql"
const kind string = "dgraph-dql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
DgraphClient() *dgraph.DgraphClient
@@ -51,7 +66,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -64,7 +79,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.McpManifest{
@@ -76,7 +91,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,

View File

@@ -50,7 +50,7 @@ func TestParseFromYamlDgraph(t *testing.T) {
want: server.ToolConfigs{
"example_tool": dgraph.Config{
Name: "example_tool",
Kind: dgraph.ToolKind,
Kind: "dgraph-dql",
Source: "my-dgraph-instance",
AuthRequired: []string{},
Description: "some tool description",
@@ -74,7 +74,7 @@ func TestParseFromYamlDgraph(t *testing.T) {
want: server.ToolConfigs{
"example_tool": dgraph.Config{
Name: "example_tool",
Kind: dgraph.ToolKind,
Kind: "dgraph-dql",
Source: "my-dgraph-instance",
Description: "some tool description",
AuthRequired: []string{},

View File

@@ -27,12 +27,27 @@ import (
"maps"
"text/template"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
httpsrc "github.com/googleapis/genai-toolbox/internal/sources/http"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "http"
const kind string = "http"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type Config struct {
Name string `yaml:"name" validate:"required"`
@@ -53,7 +68,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -66,7 +81,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(*httpsrc.Source)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be `http`", ToolKind)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be `http`", kind)
}
// Create URL based on BaseURL and Path
@@ -153,7 +168,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
URL: u,
Method: cfg.Method,
AuthRequired: cfg.AuthRequired,

View File

@@ -82,7 +82,7 @@ func TestParseFromYamlHTTP(t *testing.T) {
want: server.ToolConfigs{
"example_tool": http.Config{
Name: "example_tool",
Kind: http.ToolKind,
Kind: "http",
Source: "my-instance",
Method: "GET",
Path: "search?name=alice&pet=cat",

View File

@@ -19,13 +19,28 @@ import (
"database/sql"
"fmt"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/cloudsqlmssql"
"github.com/googleapis/genai-toolbox/internal/sources/mssql"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "mssql-execute-sql"
const kind string = "mssql-execute-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
MSSQLDB() *sql.DB
@@ -49,7 +64,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -62,7 +77,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
@@ -77,7 +92,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Pool: s.MSSQLDB(),

View File

@@ -49,7 +49,7 @@ func TestParseFromYamlExecuteSql(t *testing.T) {
want: server.ToolConfigs{
"example_tool": mssqlexecutesql.Config{
Name: "example_tool",
Kind: mssqlexecutesql.ToolKind,
Kind: "mssql-execute-sql",
Source: "my-instance",
Description: "some description",
AuthRequired: []string{"my-google-auth-service", "other-auth-service"},

View File

@@ -20,13 +20,28 @@ import (
"fmt"
"strings"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/cloudsqlmssql"
"github.com/googleapis/genai-toolbox/internal/sources/mssql"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "mssql-sql"
const kind string = "mssql-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
MSSQLDB() *sql.DB
@@ -52,7 +67,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -65,7 +80,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.McpManifest{
@@ -77,7 +92,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,

View File

@@ -61,7 +61,7 @@ func TestParseFromYamlMssql(t *testing.T) {
want: server.ToolConfigs{
"example_tool": mssqlsql.Config{
Name: "example_tool",
Kind: mssqlsql.ToolKind,
Kind: "mssql-sql",
Source: "my-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",

View File

@@ -19,13 +19,28 @@ import (
"database/sql"
"fmt"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/cloudsqlmysql"
"github.com/googleapis/genai-toolbox/internal/sources/mysql"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "mysql-execute-sql"
const kind string = "mysql-execute-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
MySQLPool() *sql.DB
@@ -49,7 +64,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -62,7 +77,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
@@ -77,7 +92,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Pool: s.MySQLPool(),

View File

@@ -49,7 +49,7 @@ func TestParseFromYamlExecuteSql(t *testing.T) {
want: server.ToolConfigs{
"example_tool": mysqlexecutesql.Config{
Name: "example_tool",
Kind: mysqlexecutesql.ToolKind,
Kind: "mysql-execute-sql",
Source: "my-instance",
Description: "some description",
AuthRequired: []string{"my-google-auth-service", "other-auth-service"},

View File

@@ -19,13 +19,28 @@ import (
"database/sql"
"fmt"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/cloudsqlmysql"
"github.com/googleapis/genai-toolbox/internal/sources/mysql"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "mysql-sql"
const kind string = "mysql-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
MySQLPool() *sql.DB
@@ -51,7 +66,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -64,7 +79,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.McpManifest{
@@ -76,7 +91,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,

View File

@@ -61,7 +61,7 @@ func TestParseFromYamlMySQL(t *testing.T) {
want: server.ToolConfigs{
"example_tool": mysqlsql.Config{
Name: "example_tool",
Kind: mysqlsql.ToolKind,
Kind: "mysql-sql",
Source: "my-mysql-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",

View File

@@ -18,6 +18,7 @@ import (
"context"
"fmt"
yaml "github.com/goccy/go-yaml"
neo4jsc "github.com/googleapis/genai-toolbox/internal/sources/neo4j"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
@@ -25,7 +26,21 @@ import (
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "neo4j-cypher"
const kind string = "neo4j-cypher"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
Neo4jDriver() neo4j.DriverWithContext
@@ -51,7 +66,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -64,7 +79,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.McpManifest{
@@ -76,7 +91,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,

View File

@@ -56,7 +56,7 @@ func TestParseFromYamlNeo4j(t *testing.T) {
want: server.ToolConfigs{
"example_tool": neo4j.Config{
Name: "example_tool",
Kind: neo4j.ToolKind,
Kind: "neo4j-cypher",
Source: "my-neo4j-instance",
Description: "some tool description",
AuthRequired: []string{"my-google-auth-service", "other-auth-service"},

View File

@@ -18,6 +18,7 @@ import (
"context"
"fmt"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/alloydbpg"
"github.com/googleapis/genai-toolbox/internal/sources/cloudsqlpg"
@@ -26,7 +27,21 @@ import (
"github.com/jackc/pgx/v5/pgxpool"
)
const ToolKind string = "postgres-execute-sql"
const kind string = "postgres-execute-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
PostgresPool() *pgxpool.Pool
@@ -51,7 +66,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -64,7 +79,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
@@ -79,7 +94,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Pool: s.PostgresPool(),

View File

@@ -49,7 +49,7 @@ func TestParseFromYamlExecuteSql(t *testing.T) {
want: server.ToolConfigs{
"example_tool": postgresexecutesql.Config{
Name: "example_tool",
Kind: postgresexecutesql.ToolKind,
Kind: "postgres-execute-sql",
Source: "my-instance",
Description: "some description",
AuthRequired: []string{"my-google-auth-service", "other-auth-service"},

View File

@@ -18,6 +18,7 @@ import (
"context"
"fmt"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/alloydbpg"
"github.com/googleapis/genai-toolbox/internal/sources/cloudsqlpg"
@@ -26,7 +27,21 @@ import (
"github.com/jackc/pgx/v5/pgxpool"
)
const ToolKind string = "postgres-sql"
const kind string = "postgres-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
PostgresPool() *pgxpool.Pool
@@ -53,7 +68,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -66,7 +81,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.McpManifest{
@@ -78,7 +93,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,

View File

@@ -61,7 +61,7 @@ func TestParseFromYamlPostgres(t *testing.T) {
want: server.ToolConfigs{
"example_tool": postgressql.Config{
Name: "example_tool",
Kind: postgressql.ToolKind,
Kind: "postgres-sql",
Source: "my-pg-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",

View File

@@ -20,13 +20,28 @@ import (
"strings"
"cloud.google.com/go/spanner"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
spannerdb "github.com/googleapis/genai-toolbox/internal/sources/spanner"
"github.com/googleapis/genai-toolbox/internal/tools"
"google.golang.org/api/iterator"
)
const ToolKind string = "spanner-sql"
const kind string = "spanner-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
SpannerClient() *spanner.Client
@@ -53,7 +68,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -66,7 +81,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.McpManifest{
@@ -78,7 +93,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,

View File

@@ -53,7 +53,7 @@ func TestParseFromYamlSpanner(t *testing.T) {
want: server.ToolConfigs{
"example_tool": spanner.Config{
Name: "example_tool",
Kind: spanner.ToolKind,
Kind: "spanner-sql",
Source: "my-pg-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",
@@ -83,7 +83,7 @@ func TestParseFromYamlSpanner(t *testing.T) {
want: server.ToolConfigs{
"example_tool": spanner.Config{
Name: "example_tool",
Kind: spanner.ToolKind,
Kind: "spanner-sql",
Source: "my-pg-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",

View File

@@ -19,13 +19,28 @@ import (
"fmt"
"cloud.google.com/go/spanner"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
spannerdb "github.com/googleapis/genai-toolbox/internal/sources/spanner"
"github.com/googleapis/genai-toolbox/internal/tools"
"google.golang.org/api/iterator"
)
const ToolKind string = "spanner-execute-sql"
const kind string = "spanner-execute-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
SpannerClient() *spanner.Client
@@ -50,7 +65,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -63,7 +78,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
@@ -78,7 +93,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
ReadOnly: cfg.ReadOnly,

View File

@@ -46,7 +46,7 @@ func TestParseFromYamlExecuteSql(t *testing.T) {
want: server.ToolConfigs{
"example_tool": spannerexecutesql.Config{
Name: "example_tool",
Kind: spannerexecutesql.ToolKind,
Kind: "spanner-execute-sql",
Source: "my-spanner-instance",
Description: "some description",
AuthRequired: []string{},
@@ -67,7 +67,7 @@ func TestParseFromYamlExecuteSql(t *testing.T) {
want: server.ToolConfigs{
"example_tool": spannerexecutesql.Config{
Name: "example_tool",
Kind: spannerexecutesql.ToolKind,
Kind: "spanner-execute-sql",
Source: "my-spanner-instance",
Description: "some description",
AuthRequired: []string{},

View File

@@ -19,12 +19,27 @@ import (
"database/sql"
"fmt"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/sqlite"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const ToolKind string = "sqlite-sql"
const kind string = "sqlite-sql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
SQLiteDB() *sql.DB
@@ -49,7 +64,7 @@ type Config struct {
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigKind() string {
return ToolKind
return kind
}
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
@@ -62,7 +77,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", ToolKind, compatibleSources)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.McpManifest{
@@ -74,7 +89,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: ToolKind,
Kind: kind,
Parameters: cfg.Parameters,
Statement: cfg.Statement,
AuthRequired: cfg.AuthRequired,

View File

@@ -61,7 +61,7 @@ func TestParseFromYamlSQLite(t *testing.T) {
want: server.ToolConfigs{
"example_tool": sqlitesql.Config{
Name: "example_tool",
Kind: sqlitesql.ToolKind,
Kind: "sqlite-sql",
Source: "my-sqlite-instance",
Description: "some description",
Statement: "SELECT * FROM SQL_STATEMENT;\n",

View File

@@ -16,11 +16,48 @@ package tools
import (
"context"
"fmt"
"slices"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
)
// ToolConfigFactory defines the signature for a function that creates and
// decodes a specific tool's configuration. It takes the context, the tool's
// name, and a YAML decoder to parse the config.
type ToolConfigFactory func(ctx context.Context, name string, decoder *yaml.Decoder) (ToolConfig, error)
var toolRegistry = make(map[string]ToolConfigFactory)
// Register allows individual tool packages to register their configuration
// factory function. This is typically called from an init() function in the
// tool's package. It associates a 'kind' string with a function that can
// produce the specific ToolConfig type. It returns true if the registration was
// successful, and false if a tool with the same kind was already registered.
func Register(kind string, factory ToolConfigFactory) bool {
if _, exists := toolRegistry[kind]; exists {
// Tool with this kind already exists, do not overwrite.
return false
}
toolRegistry[kind] = factory
return true
}
// DecodeConfig looks up the registered factory for the given kind and uses it
// to decode the tool configuration.
func DecodeConfig(ctx context.Context, kind string, name string, decoder *yaml.Decoder) (ToolConfig, error) {
factory, found := toolRegistry[kind]
if !found {
return nil, fmt.Errorf("unknown tool kind: %q", kind)
}
toolConfig, err := factory(ctx, name, decoder)
if err != nil {
return nil, fmt.Errorf("unable to parse tool %q as kind %q: %w", name, kind, err)
}
return toolConfig, nil
}
type ToolConfig interface {
ToolConfigKind() string
Initialize(map[string]sources.Source) (Tool, error)

View File

@@ -14,7 +14,9 @@
package main
import "github.com/googleapis/genai-toolbox/cmd"
import (
"github.com/googleapis/genai-toolbox/cmd"
)
func main() {
cmd.Execute()