diff --git a/internal/tools/postgres/postgresdatabaseoverview/postgresdatabaseoverview.go b/internal/tools/postgres/postgresdatabaseoverview/postgresdatabaseoverview.go index 4e8a0a29ce5..7366fdb24b3 100644 --- a/internal/tools/postgres/postgresdatabaseoverview/postgresdatabaseoverview.go +++ b/internal/tools/postgres/postgresdatabaseoverview/postgresdatabaseoverview.go @@ -60,11 +60,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -79,7 +80,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) if cfg.Description == "" { cfg.Description = "Fetches the current state of the PostgreSQL server, returning the version, whether it's a replica, uptime duration, maximum connection limit, number of current connections, number of active connections, and the percentage of connections in use." } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgresexecutesql/postgresexecutesql.go b/internal/tools/postgres/postgresexecutesql/postgresexecutesql.go index 73afd2a6ee1..338805328c6 100644 --- a/internal/tools/postgres/postgresexecutesql/postgresexecutesql.go +++ b/internal/tools/postgres/postgresexecutesql/postgresexecutesql.go @@ -48,11 +48,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description" validate:"required"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description" validate:"required"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -66,7 +67,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) sqlParameter := parameters.NewStringParameter("sql", "The sql to execute.") params := parameters.Parameters{sqlParameter} - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewDestructiveAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, annotations) // finish tool setup t := Tool{ diff --git a/internal/tools/postgres/postgresgetcolumncardinality/postgresgetcolumncardinality.go b/internal/tools/postgres/postgresgetcolumncardinality/postgresgetcolumncardinality.go index f96654fbc6b..9a60319f8de 100644 --- a/internal/tools/postgres/postgresgetcolumncardinality/postgresgetcolumncardinality.go +++ b/internal/tools/postgres/postgresgetcolumncardinality/postgresgetcolumncardinality.go @@ -65,11 +65,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -91,7 +92,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) cfg.Description = "Estimates the number of unique values (cardinality) quickly for one or all columns in a specific PostgreSQL table by using the database's internal statistics, returning the results in descending order of estimated cardinality. Please run ANALYZE on the table before using this tool to get accurate results. The tool returns the column_name and the estimated_cardinality. If the column_name is not provided, the tool returns all columns along with their estimated cardinality." } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslistactivequeries/postgreslistactivequeries.go b/internal/tools/postgres/postgreslistactivequeries/postgreslistactivequeries.go index 6ad5bff569c..88d9b718950 100644 --- a/internal/tools/postgres/postgreslistactivequeries/postgreslistactivequeries.go +++ b/internal/tools/postgres/postgreslistactivequeries/postgreslistactivequeries.go @@ -69,11 +69,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description" validate:"required"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description" validate:"required"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -90,7 +91,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) parameters.NewIntParameterWithDefault("limit", 50, "Optional: The maximum number of rows to return."), } paramManifest := allParameters.Manifest() - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup t := Tool{ diff --git a/internal/tools/postgres/postgreslistavailableextensions/postgreslistavailableextensions.go b/internal/tools/postgres/postgreslistavailableextensions/postgreslistavailableextensions.go index 1440509cbbf..3c308717139 100644 --- a/internal/tools/postgres/postgreslistavailableextensions/postgreslistavailableextensions.go +++ b/internal/tools/postgres/postgreslistavailableextensions/postgreslistavailableextensions.go @@ -56,11 +56,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description" validate:"required"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description" validate:"required"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -72,7 +73,8 @@ func (cfg Config) ToolConfigKind() string { func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) { params := parameters.Parameters{} - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, annotations) // finish tool setup t := Tool{ diff --git a/internal/tools/postgres/postgreslistdatabasestats/postgreslistdatabasestats.go b/internal/tools/postgres/postgreslistdatabasestats/postgreslistdatabasestats.go index 27cc16c1ed7..59561cd6d5f 100644 --- a/internal/tools/postgres/postgreslistdatabasestats/postgreslistdatabasestats.go +++ b/internal/tools/postgres/postgreslistdatabasestats/postgreslistdatabasestats.go @@ -113,11 +113,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -160,7 +161,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) "number of active connections to the database, the timestamp of the " + "last statistics reset, and total database size in bytes." } - mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslistindexes/postgreslistindexes.go b/internal/tools/postgres/postgreslistindexes/postgreslistindexes.go index 0f85a0e46ce..5e9a4cef420 100644 --- a/internal/tools/postgres/postgreslistindexes/postgreslistindexes.go +++ b/internal/tools/postgres/postgreslistindexes/postgreslistindexes.go @@ -92,11 +92,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -118,7 +119,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) if cfg.Description == "" { cfg.Description = "Lists available user indexes in the database, excluding system schemas (pg_catalog, information_schema). For each index, the following properties are returned: schema name, table name, index name, index type (access method), a boolean indicating if it's a unique index, a boolean indicating if it's for a primary key, the index definition, index size in bytes, the number of index scans, the number of index tuples read, the number of table tuples fetched via index scans, and a boolean indicating if the index has been used at least once." } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslistinstalledextensions/postgreslistinstalledextensions.go b/internal/tools/postgres/postgreslistinstalledextensions/postgreslistinstalledextensions.go index effa306f463..f4e77e8919a 100644 --- a/internal/tools/postgres/postgreslistinstalledextensions/postgreslistinstalledextensions.go +++ b/internal/tools/postgres/postgreslistinstalledextensions/postgreslistinstalledextensions.go @@ -67,11 +67,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description" validate:"required"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description" validate:"required"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -83,7 +84,8 @@ func (cfg Config) ToolConfigKind() string { func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) { params := parameters.Parameters{} - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, annotations) // finish tool setup t := Tool{ diff --git a/internal/tools/postgres/postgreslistlocks/postgreslistlocks.go b/internal/tools/postgres/postgreslistlocks/postgreslistlocks.go index 881962e2bed..dfe8a50ad1a 100644 --- a/internal/tools/postgres/postgreslistlocks/postgreslistlocks.go +++ b/internal/tools/postgres/postgreslistlocks/postgreslistlocks.go @@ -67,11 +67,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -89,7 +90,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) cfg.Description = "Identifies all locks held by active processes showing the process ID, user, query text, and an aggregated list of all transactions and specific locks (relation, mode, grant status) associated with each process." } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslistpgsettings/postgreslistpgsettings.go b/internal/tools/postgres/postgreslistpgsettings/postgreslistpgsettings.go index 05fccc3d6e4..313d46c6ecf 100644 --- a/internal/tools/postgres/postgreslistpgsettings/postgreslistpgsettings.go +++ b/internal/tools/postgres/postgreslistpgsettings/postgreslistpgsettings.go @@ -65,11 +65,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -88,7 +89,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) if description == "" { description = "Lists configuration parameters for the postgres server ordered lexicographically, with a default limit of 50 rows. It returns the parameter name, its current setting, unit of measurement, a short description, the source of the current setting (e.g., default, configuration file, session), and whether a restart is required when the parameter value is changed." } - mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslistpublicationtables/postgreslistpublicationtables.go b/internal/tools/postgres/postgreslistpublicationtables/postgreslistpublicationtables.go index 9b1d48fdea3..78f0e4948b0 100644 --- a/internal/tools/postgres/postgreslistpublicationtables/postgreslistpublicationtables.go +++ b/internal/tools/postgres/postgreslistpublicationtables/postgreslistpublicationtables.go @@ -76,11 +76,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -101,7 +102,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) if description == "" { description = "Lists all publication tables in the database. Returns the publication name, schema name, and table name, along with definition details indicating if it publishes all tables, whether it replicates inserts, updates, deletes, or truncates, and the publication owner." } - mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslistquerystats/postgreslistquerystats.go b/internal/tools/postgres/postgreslistquerystats/postgreslistquerystats.go index e2a26e496ba..a50542ea323 100644 --- a/internal/tools/postgres/postgreslistquerystats/postgreslistquerystats.go +++ b/internal/tools/postgres/postgreslistquerystats/postgreslistquerystats.go @@ -66,11 +66,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -91,7 +92,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) cfg.Description = "Lists performance statistics for executed queries ordered by total time, filtering by database name pattern if provided. This tool requires the pg_stat_statements extension to be installed. The tool returns the database name, query text, execution count, timing metrics (total, min, max, mean), rows affected, and buffer cache I/O statistics (hits and reads)." } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslistroles/postgreslistroles.go b/internal/tools/postgres/postgreslistroles/postgreslistroles.go index 160aebb31a5..98bfbf7dc64 100644 --- a/internal/tools/postgres/postgreslistroles/postgreslistroles.go +++ b/internal/tools/postgres/postgreslistroles/postgreslistroles.go @@ -88,11 +88,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -112,7 +113,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) if description == "" { description = "Lists all the user-created roles in the instance . It returns the role name, Object ID, the maximum number of concurrent connections the role can make, along with boolean indicators for: superuser status, privilege inheritance from member roles, ability to create roles, ability to create databases, ability to log in, replication privilege, and the ability to bypass row-level security, the password expiration timestamp, a list of direct members belonging to this role, and a list of other roles/groups that this role is a member of." } - mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslistschemas/postgreslistschemas.go b/internal/tools/postgres/postgreslistschemas/postgreslistschemas.go index 729a4af1b45..358562455d2 100644 --- a/internal/tools/postgres/postgreslistschemas/postgreslistschemas.go +++ b/internal/tools/postgres/postgreslistschemas/postgreslistschemas.go @@ -100,11 +100,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -124,7 +125,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) if cfg.Description == "" { cfg.Description = "Lists all schemas in the database ordered by schema name and excluding system and temporary schemas. It returns the schema name, schema owner, grants, number of functions, number of tables and number of views within each schema." } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslistsequences/postgreslistsequences.go b/internal/tools/postgres/postgreslistsequences/postgreslistsequences.go index a8877ab6f7f..eb5a71b1f96 100644 --- a/internal/tools/postgres/postgreslistsequences/postgreslistsequences.go +++ b/internal/tools/postgres/postgreslistsequences/postgreslistsequences.go @@ -66,11 +66,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -90,7 +91,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) if cfg.Description == "" { cfg.Description = "Lists sequences in the database. Returns sequence name, schema name, sequence owner, data type of the sequence, starting value, minimum value, maximum value of the sequence, the value by which the sequence is incremented, and the last value generated by the sequence in the current session" } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslisttables/postgreslisttables.go b/internal/tools/postgres/postgreslisttables/postgreslisttables.go index 264983edb66..4ae5ea6b351 100644 --- a/internal/tools/postgres/postgreslisttables/postgreslisttables.go +++ b/internal/tools/postgres/postgreslisttables/postgreslisttables.go @@ -124,11 +124,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description" validate:"required"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description" validate:"required"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -144,7 +145,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) parameters.NewStringParameterWithDefault("output_format", "detailed", "Optional: Use 'simple' for names only or 'detailed' for full info."), } paramManifest := allParameters.Manifest() - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) t := Tool{ Config: cfg, diff --git a/internal/tools/postgres/postgreslisttablespaces/postgreslisttablespaces.go b/internal/tools/postgres/postgreslisttablespaces/postgreslisttablespaces.go index 8e2d0e700df..3255c077da5 100644 --- a/internal/tools/postgres/postgreslisttablespaces/postgreslisttablespaces.go +++ b/internal/tools/postgres/postgreslisttablespaces/postgreslisttablespaces.go @@ -72,11 +72,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -95,7 +96,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) if description == "" { description = "Lists all tablespaces in the database. Returns the tablespace name, owner name, size in bytes(if the current user has CREATE privileges on the tablespace, otherwise NULL), internal object ID, the access control list regarding permissions, and any specific tablespace options." } - mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslisttablestats/postgreslisttablestats.go b/internal/tools/postgres/postgreslisttablestats/postgreslisttablestats.go index 69a953e6548..14952ad3885 100644 --- a/internal/tools/postgres/postgreslisttablestats/postgreslisttablestats.go +++ b/internal/tools/postgres/postgreslisttablestats/postgreslisttablestats.go @@ -93,11 +93,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -129,7 +130,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) for the last_vacuum, last_autovacuum, and last_autoanalyze operations.` } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslisttriggers/postgreslisttriggers.go b/internal/tools/postgres/postgreslisttriggers/postgreslisttriggers.go index 8fc4944f736..32df512a323 100644 --- a/internal/tools/postgres/postgreslisttriggers/postgreslisttriggers.go +++ b/internal/tools/postgres/postgreslisttriggers/postgreslisttriggers.go @@ -92,11 +92,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -117,7 +118,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) if cfg.Description == "" { cfg.Description = "Lists all non-internal triggers in a database. Returns trigger name, schema name, table name, whether its enabled or disabled, timing (e.g BEFORE/AFTER of the event), the events that cause the trigger to fire such as INSERT, UPDATE, or DELETE, whether the trigger activates per ROW or per STATEMENT, the handler function executed by the trigger and full definition." } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslistviews/postgreslistviews.go b/internal/tools/postgres/postgreslistviews/postgreslistviews.go index d0aa2438d12..f2c7ee2a283 100644 --- a/internal/tools/postgres/postgreslistviews/postgreslistviews.go +++ b/internal/tools/postgres/postgreslistviews/postgreslistviews.go @@ -67,11 +67,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -91,7 +92,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) if cfg.Description == "" { cfg.Description = "Lists views in the database from pg_views with a default limit of 50 rows. Returns schemaname, viewname, ownername and the definition." } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgreslongrunningtransactions/postgreslongrunningtransactions.go b/internal/tools/postgres/postgreslongrunningtransactions/postgreslongrunningtransactions.go index 1b2434679d0..ce619aea6f1 100644 --- a/internal/tools/postgres/postgreslongrunningtransactions/postgreslongrunningtransactions.go +++ b/internal/tools/postgres/postgreslongrunningtransactions/postgreslongrunningtransactions.go @@ -74,11 +74,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -99,7 +100,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) cfg.Description = "Identifies and lists database transactions that exceed a specified time limit. For each of the long running transactions, the output contains the process id, database name, user name, application name, client address, state, connection age, transaction age, query age, last activity age, wait event type, wait event, and query string." } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgresreplicationstats/postgresreplicationstats.go b/internal/tools/postgres/postgresreplicationstats/postgresreplicationstats.go index 4280f1a0a34..b75e69f0f16 100644 --- a/internal/tools/postgres/postgresreplicationstats/postgresreplicationstats.go +++ b/internal/tools/postgres/postgresreplicationstats/postgresreplicationstats.go @@ -64,11 +64,12 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description"` - AuthRequired []string `yaml:"authRequired"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description"` + AuthRequired []string `yaml:"authRequired"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -86,7 +87,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) cfg.Description = "Lists each replica's process ID, user name, application name, backend_xmin (standby's xmin horizon reported by hot_standby_feedback), client IP address, connection state, and sync_state, along with lag sizes in bytes for sent_lag (primary to sent), write_lag (sent to written), flush_lag (written to flushed), replay_lag (flushed to replayed), and the overall total_lag (primary to replayed)." } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup return Tool{ diff --git a/internal/tools/postgres/postgressql/postgressql.go b/internal/tools/postgres/postgressql/postgressql.go index 1de22a5a827..7aeec583dc6 100644 --- a/internal/tools/postgres/postgressql/postgressql.go +++ b/internal/tools/postgres/postgressql/postgressql.go @@ -46,14 +46,15 @@ type compatibleSource interface { } type Config struct { - Name string `yaml:"name" validate:"required"` - Kind string `yaml:"kind" validate:"required"` - Source string `yaml:"source" validate:"required"` - Description string `yaml:"description" validate:"required"` - Statement string `yaml:"statement" validate:"required"` - AuthRequired []string `yaml:"authRequired"` - Parameters parameters.Parameters `yaml:"parameters"` - TemplateParameters parameters.Parameters `yaml:"templateParameters"` + Name string `yaml:"name" validate:"required"` + Kind string `yaml:"kind" validate:"required"` + Source string `yaml:"source" validate:"required"` + Description string `yaml:"description" validate:"required"` + Statement string `yaml:"statement" validate:"required"` + AuthRequired []string `yaml:"authRequired"` + Parameters parameters.Parameters `yaml:"parameters"` + TemplateParameters parameters.Parameters `yaml:"templateParameters"` + Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"` } // validate interface @@ -69,7 +70,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) return nil, err } - mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil) + annotations := tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations) + mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, annotations) // finish tool setup t := Tool{ diff --git a/internal/tools/tools.go b/internal/tools/tools.go index 7283655f0c3..3e2d8240222 100644 --- a/internal/tools/tools.go +++ b/internal/tools/tools.go @@ -74,6 +74,33 @@ type ToolAnnotations struct { ReadOnlyHint *bool `json:"readOnlyHint,omitempty" yaml:"readOnlyHint,omitempty"` } +// NewReadOnlyAnnotations creates default annotations for a read-only tool. +// Use this for tools that only query/fetch data without side effects. +func NewReadOnlyAnnotations() *ToolAnnotations { + readOnly := true + return &ToolAnnotations{ReadOnlyHint: &readOnly} +} + +// NewDestructiveAnnotations creates default annotations for a destructive tool. +// Use this for tools that create, update, or delete data. +func NewDestructiveAnnotations() *ToolAnnotations { + readOnly := false + destructive := true + return &ToolAnnotations{ + ReadOnlyHint: &readOnly, + DestructiveHint: &destructive, + } +} + +// GetAnnotationsOrDefault returns the provided annotations if non-nil, +// otherwise returns the result of calling defaultFn. +func GetAnnotationsOrDefault(annotations *ToolAnnotations, defaultFn func() *ToolAnnotations) *ToolAnnotations { + if annotations != nil { + return annotations + } + return defaultFn() +} + type AccessToken string func (token AccessToken) ParseBearerToken() (string, error) {