Skip to main content
Glama

MCP Toolbox for Databases

by googleapis
Apache 2.0
11,037
  • Linux
parameters.go36.2 kB
// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tools import ( "bytes" "context" "encoding/json" "fmt" "slices" "strings" "text/template" "github.com/googleapis/genai-toolbox/internal/util" ) const ( typeString = "string" typeInt = "integer" typeFloat = "float" typeBool = "boolean" typeArray = "array" typeMap = "map" ) // ParamValues is an ordered list of ParamValue type ParamValues []ParamValue // ParamValue represents the parameter's name and value. type ParamValue struct { Name string Value any } // AsSlice returns a slice of the Param's values (in order). func (p ParamValues) AsSlice() []any { params := []any{} for _, p := range p { params = append(params, p.Value) } return params } // AsMap returns a map of ParamValue's names to values. func (p ParamValues) AsMap() map[string]interface{} { params := make(map[string]interface{}) for _, p := range p { params[p.Name] = p.Value } return params } // AsMapByOrderedKeys returns a map of a key's position to it's value, as necessary for Spanner PSQL. // Example { $1 -> "value1", $2 -> "value2" } func (p ParamValues) AsMapByOrderedKeys() map[string]interface{} { params := make(map[string]interface{}) for i, p := range p { key := fmt.Sprintf("p%d", i+1) params[key] = p.Value } return params } // AsMapWithDollarPrefix ensures all keys are prefixed with a dollar sign for Dgraph. // Example: // Input: {"role": "admin", "$age": 30} // Output: {"$role": "admin", "$age": 30} func (p ParamValues) AsMapWithDollarPrefix() map[string]interface{} { params := make(map[string]interface{}) for _, param := range p { key := param.Name if !strings.HasPrefix(key, "$") { key = "$" + key } params[key] = param.Value } return params } func parseFromAuthService(paramAuthServices []ParamAuthService, claimsMap map[string]map[string]any) (any, error) { // parse a parameter from claims using its specified auth services for _, a := range paramAuthServices { claims, ok := claimsMap[a.Name] if !ok { // not validated for this authservice, skip to the next one continue } v, ok := claims[a.Field] if !ok { // claims do not contain specified field return nil, fmt.Errorf("no field named %s in claims", a.Field) } return v, nil } return nil, fmt.Errorf("missing or invalid authentication header: %w", ErrUnauthorized) } // CheckParamRequired checks if a parameter is required based on the required and default field. func CheckParamRequired(required bool, defaultV any) bool { return required && defaultV == nil } // ParseParams is a helper function for parsing Parameters from an arbitraryJSON object. func ParseParams(ps Parameters, data map[string]any, claimsMap map[string]map[string]any) (ParamValues, error) { params := make([]ParamValue, 0, len(ps)) for _, p := range ps { var v, newV any var err error paramAuthServices := p.GetAuthServices() name := p.GetName() if len(paramAuthServices) == 0 { // parse non auth-required parameter var ok bool v, ok = data[name] if !ok { v = p.GetDefault() // if the parameter is required and no value given, throw an error if CheckParamRequired(p.GetRequired(), v) { return nil, fmt.Errorf("parameter %q is required", name) } } } else { // parse authenticated parameter v, err = parseFromAuthService(paramAuthServices, claimsMap) if err != nil { return nil, fmt.Errorf("error parsing authenticated parameter %q: %w", name, err) } } if v != nil { newV, err = p.Parse(v) if err != nil { return nil, fmt.Errorf("unable to parse value for %q: %w", name, err) } } params = append(params, ParamValue{Name: name, Value: newV}) } return params, nil } // helper function to convert a string array parameter to a comma separated string func ConvertArrayParamToString(param any) (string, error) { switch v := param.(type) { case []any: var stringValues []string for _, item := range v { stringVal, ok := item.(string) if !ok { return "", fmt.Errorf("templateParameter only supports string arrays") } stringValues = append(stringValues, stringVal) } return strings.Join(stringValues, ", "), nil default: return "", fmt.Errorf("invalid parameter type, expected array of type string") } } // GetParams return the ParamValues that are associated with the Parameters. func GetParams(params Parameters, paramValuesMap map[string]any) (ParamValues, error) { resultParamValues := make(ParamValues, 0) for _, p := range params { k := p.GetName() v, ok := paramValuesMap[k] if !ok { return nil, fmt.Errorf("missing parameter %s", k) } resultParamValues = append(resultParamValues, ParamValue{Name: k, Value: v}) } return resultParamValues, nil } func ResolveTemplateParams(templateParams Parameters, originalStatement string, paramsMap map[string]any) (string, error) { templateParamsValues, err := GetParams(templateParams, paramsMap) templateParamsMap := templateParamsValues.AsMap() if err != nil { return "", fmt.Errorf("error getting template params %s", err) } funcMap := template.FuncMap{ "array": ConvertArrayParamToString, } t, err := template.New("statement").Funcs(funcMap).Parse(originalStatement) if err != nil { return "", fmt.Errorf("error creating go template %s", err) } var result bytes.Buffer err = t.Execute(&result, templateParamsMap) if err != nil { return "", fmt.Errorf("error executing go template %s", err) } modifiedStatement := result.String() return modifiedStatement, nil } // ProcessParameters concatenate templateParameters and parameters from a tool. // It returns a list of concatenated parameters, concatenated Toolbox manifest, and concatenated MCP Manifest. func ProcessParameters(templateParams Parameters, params Parameters) (Parameters, []ParameterManifest, error) { allParameters := slices.Concat(params, templateParams) // verify no duplicate parameter names err := CheckDuplicateParameters(allParameters) if err != nil { return nil, nil, err } // create Toolbox manifest paramManifest := allParameters.Manifest() if paramManifest == nil { paramManifest = make([]ParameterManifest, 0) } return allParameters, paramManifest, nil } type Parameter interface { // Note: It's typically not idiomatic to include "Get" in the function name, // but this is done to differentiate it from the fields in CommonParameter. GetName() string GetType() string GetDefault() any GetRequired() bool GetAuthServices() []ParamAuthService Parse(any) (any, error) Manifest() ParameterManifest McpManifest() (ParameterMcpManifest, []string) } // McpToolsSchema is the representation of input schema for McpManifest. type McpToolsSchema struct { Type string `json:"type"` Properties map[string]ParameterMcpManifest `json:"properties"` Required []string `json:"required"` } // Parameters is a type used to allow unmarshal a list of parameters type Parameters []Parameter func (c *Parameters) UnmarshalYAML(ctx context.Context, unmarshal func(interface{}) error) error { *c = make(Parameters, 0) var rawList []util.DelayedUnmarshaler if err := unmarshal(&rawList); err != nil { return err } for _, u := range rawList { p, err := parseParamFromDelayedUnmarshaler(ctx, &u) if err != nil { return err } (*c) = append((*c), p) } return nil } // parseParamFromDelayedUnmarshaler is a helper function that is required to parse // parameters because there are multiple different types func parseParamFromDelayedUnmarshaler(ctx context.Context, u *util.DelayedUnmarshaler) (Parameter, error) { var p map[string]any err := u.Unmarshal(&p) if err != nil { return nil, fmt.Errorf("error parsing parameters: %w", err) } t, ok := p["type"] if !ok { return nil, fmt.Errorf("parameter is missing 'type' field: %w", err) } dec, err := util.NewStrictDecoder(p) if err != nil { return nil, fmt.Errorf("error creating decoder: %w", err) } logger, err := util.LoggerFromContext(ctx) if err != nil { return nil, err } switch t { case typeString: a := &StringParameter{} if err := dec.DecodeContext(ctx, a); err != nil { return nil, fmt.Errorf("unable to parse as %q: %w", t, err) } if a.AuthSources != nil { logger.WarnContext(ctx, "`authSources` is deprecated, use `authServices` for parameters instead") a.AuthServices = append(a.AuthServices, a.AuthSources...) a.AuthSources = nil } return a, nil case typeInt: a := &IntParameter{} if err := dec.DecodeContext(ctx, a); err != nil { return nil, fmt.Errorf("unable to parse as %q: %w", t, err) } if a.AuthSources != nil { logger.WarnContext(ctx, "`authSources` is deprecated, use `authServices` for parameters instead") a.AuthServices = append(a.AuthServices, a.AuthSources...) a.AuthSources = nil } return a, nil case typeFloat: a := &FloatParameter{} if err := dec.DecodeContext(ctx, a); err != nil { return nil, fmt.Errorf("unable to parse as %q: %w", t, err) } if a.AuthSources != nil { logger.WarnContext(ctx, "`authSources` is deprecated, use `authServices` for parameters instead") a.AuthServices = append(a.AuthServices, a.AuthSources...) a.AuthSources = nil } return a, nil case typeBool: a := &BooleanParameter{} if err := dec.DecodeContext(ctx, a); err != nil { return nil, fmt.Errorf("unable to parse as %q: %w", t, err) } if a.AuthSources != nil { logger.WarnContext(ctx, "`authSources` is deprecated, use `authServices` for parameters instead") a.AuthServices = append(a.AuthServices, a.AuthSources...) a.AuthSources = nil } return a, nil case typeArray: a := &ArrayParameter{} if err := dec.DecodeContext(ctx, a); err != nil { return nil, fmt.Errorf("unable to parse as %q: %w", t, err) } if a.AuthSources != nil { logger.WarnContext(ctx, "`authSources` is deprecated, use `authServices` for parameters instead") a.AuthServices = append(a.AuthServices, a.AuthSources...) a.AuthSources = nil } return a, nil case typeMap: a := &MapParameter{} if err := dec.DecodeContext(ctx, a); err != nil { return nil, fmt.Errorf("unable to parse as %q: %w", t, err) } if a.AuthSources != nil { logger.WarnContext(ctx, "`authSources` is deprecated, use `authServices` for parameters instead") a.AuthServices = append(a.AuthServices, a.AuthSources...) a.AuthSources = nil } return a, nil } return nil, fmt.Errorf("%q is not valid type for a parameter", t) } func (ps Parameters) Manifest() []ParameterManifest { rtn := make([]ParameterManifest, 0, len(ps)) for _, p := range ps { rtn = append(rtn, p.Manifest()) } return rtn } func (ps Parameters) McpManifest() (McpToolsSchema, map[string][]string) { properties := make(map[string]ParameterMcpManifest) required := make([]string, 0) authParam := make(map[string][]string) for _, p := range ps { name := p.GetName() paramManifest, authParamList := p.McpManifest() properties[name] = paramManifest // parameters that doesn't have a default value are added to the required field if CheckParamRequired(p.GetRequired(), p.GetDefault()) { required = append(required, name) } if len(authParamList) > 0 { authParam[name] = authParamList } } return McpToolsSchema{ Type: "object", Properties: properties, Required: required, }, authParam } // ParameterManifest represents parameters when served as part of a ToolManifest. type ParameterManifest struct { Name string `json:"name"` Type string `json:"type"` Required bool `json:"required"` Description string `json:"description"` AuthServices []string `json:"authSources"` Items *ParameterManifest `json:"items,omitempty"` AdditionalProperties any `json:"additionalProperties,omitempty"` } // ParameterMcpManifest represents properties when served as part of a ToolMcpManifest. type ParameterMcpManifest struct { Type string `json:"type"` Description string `json:"description"` Items *ParameterMcpManifest `json:"items,omitempty"` AdditionalProperties any `json:"additionalProperties,omitempty"` } // CommonParameter are default fields that are emebdding in most Parameter implementations. Embedding this stuct will give the object Name() and Type() functions. type CommonParameter struct { Name string `yaml:"name" validate:"required"` Type string `yaml:"type" validate:"required"` Desc string `yaml:"description" validate:"required"` Required *bool `yaml:"required"` AuthServices []ParamAuthService `yaml:"authServices"` AuthSources []ParamAuthService `yaml:"authSources"` // Deprecated: Kept for compatibility. } // GetName returns the name specified for the Parameter. func (p *CommonParameter) GetName() string { return p.Name } // GetType returns the type specified for the Parameter. func (p *CommonParameter) GetType() string { return p.Type } // GetRequired returns the type specified for the Parameter. func (p *CommonParameter) GetRequired() bool { // parameters are defaulted to required if p.Required == nil { return true } return *p.Required } // McpManifest returns the MCP manifest for the Parameter. func (p *CommonParameter) McpManifest() (ParameterMcpManifest, []string) { authServiceNames := getAuthServiceNames(p.AuthServices) return ParameterMcpManifest{ Type: p.Type, Description: p.Desc, }, authServiceNames } // getAuthServiceNames retrieves the list of auth services names func getAuthServiceNames(authServices []ParamAuthService) []string { authServiceNames := make([]string, len(authServices)) for i, a := range authServices { authServiceNames[i] = a.Name } return authServiceNames } // ParseTypeError is a custom error for incorrectly typed Parameters. type ParseTypeError struct { Name string Type string Value any } func (e ParseTypeError) Error() string { return fmt.Sprintf("%q not type %q", e.Value, e.Type) } type ParamAuthService struct { Name string `yaml:"name"` Field string `yaml:"field"` } // NewStringParameter is a convenience function for initializing a StringParameter. func NewStringParameter(name string, desc string) *StringParameter { return &StringParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeString, Desc: desc, AuthServices: nil, }, } } // NewStringParameterWithDefault is a convenience function for initializing a StringParameter with default value. func NewStringParameterWithDefault(name string, defaultV, desc string) *StringParameter { return &StringParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeString, Desc: desc, AuthServices: nil, }, Default: &defaultV, } } // NewStringParameterWithRequired is a convenience function for initializing a StringParameter. func NewStringParameterWithRequired(name string, desc string, required bool) *StringParameter { return &StringParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeString, Desc: desc, Required: &required, AuthServices: nil, }, } } // NewStringParameterWithAuth is a convenience function for initializing a StringParameter with a list of ParamAuthService. func NewStringParameterWithAuth(name string, desc string, authServices []ParamAuthService) *StringParameter { return &StringParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeString, Desc: desc, AuthServices: authServices, }, } } var _ Parameter = &StringParameter{} // StringParameter is a parameter representing the "string" type. type StringParameter struct { CommonParameter `yaml:",inline"` Default *string `yaml:"default"` } // Parse casts the value "v" as a "string". func (p *StringParameter) Parse(v any) (any, error) { newV, ok := v.(string) if !ok { return nil, &ParseTypeError{p.Name, p.Type, v} } return newV, nil } func (p *StringParameter) GetAuthServices() []ParamAuthService { return p.AuthServices } func (p *StringParameter) GetDefault() any { if p.Default == nil { return nil } return *p.Default } // Manifest returns the manifest for the StringParameter. func (p *StringParameter) Manifest() ParameterManifest { // only list ParamAuthService names (without fields) in manifest authServiceNames := getAuthServiceNames(p.AuthServices) r := CheckParamRequired(p.GetRequired(), p.GetDefault()) return ParameterManifest{ Name: p.Name, Type: p.Type, Required: r, Description: p.Desc, AuthServices: authServiceNames, } } // NewIntParameter is a convenience function for initializing a IntParameter. func NewIntParameter(name string, desc string) *IntParameter { return &IntParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeInt, Desc: desc, AuthServices: nil, }, } } // NewIntParameterWithDefault is a convenience function for initializing a IntParameter with default value. func NewIntParameterWithDefault(name string, defaultV int, desc string) *IntParameter { return &IntParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeInt, Desc: desc, AuthServices: nil, }, Default: &defaultV, } } // NewIntParameterWithRequired is a convenience function for initializing a IntParameter. func NewIntParameterWithRequired(name string, desc string, required bool) *IntParameter { return &IntParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeInt, Desc: desc, Required: &required, AuthServices: nil, }, } } // NewIntParameterWithAuth is a convenience function for initializing a IntParameter with a list of ParamAuthService. func NewIntParameterWithAuth(name string, desc string, authServices []ParamAuthService) *IntParameter { return &IntParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeInt, Desc: desc, AuthServices: authServices, }, } } var _ Parameter = &IntParameter{} // IntParameter is a parameter representing the "int" type. type IntParameter struct { CommonParameter `yaml:",inline"` Default *int `yaml:"default"` } func (p *IntParameter) Parse(v any) (any, error) { var out int switch newV := v.(type) { default: return nil, &ParseTypeError{p.Name, p.Type, v} case int: out = int(newV) case int32: out = int(newV) case int64: out = int(newV) case json.Number: newI, err := newV.Int64() if err != nil { return nil, &ParseTypeError{p.Name, p.Type, v} } out = int(newI) } return out, nil } func (p *IntParameter) GetAuthServices() []ParamAuthService { return p.AuthServices } func (p *IntParameter) GetDefault() any { if p.Default == nil { return nil } return *p.Default } // Manifest returns the manifest for the IntParameter. func (p *IntParameter) Manifest() ParameterManifest { // only list ParamAuthService names (without fields) in manifest authServiceNames := getAuthServiceNames(p.AuthServices) r := CheckParamRequired(p.GetRequired(), p.GetDefault()) return ParameterManifest{ Name: p.Name, Type: p.Type, Required: r, Description: p.Desc, AuthServices: authServiceNames, } } // NewFloatParameter is a convenience function for initializing a FloatParameter. func NewFloatParameter(name string, desc string) *FloatParameter { return &FloatParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeFloat, Desc: desc, AuthServices: nil, }, } } // NewFloatParameterWithDefault is a convenience function for initializing a FloatParameter with default value. func NewFloatParameterWithDefault(name string, defaultV float64, desc string) *FloatParameter { return &FloatParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeFloat, Desc: desc, AuthServices: nil, }, Default: &defaultV, } } // NewFloatParameterWithRequired is a convenience function for initializing a FloatParameter. func NewFloatParameterWithRequired(name string, desc string, required bool) *FloatParameter { return &FloatParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeFloat, Desc: desc, Required: &required, AuthServices: nil, }, } } // NewFloatParameterWithAuth is a convenience function for initializing a FloatParameter with a list of ParamAuthService. func NewFloatParameterWithAuth(name string, desc string, authServices []ParamAuthService) *FloatParameter { return &FloatParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeFloat, Desc: desc, AuthServices: authServices, }, } } var _ Parameter = &FloatParameter{} // FloatParameter is a parameter representing the "float" type. type FloatParameter struct { CommonParameter `yaml:",inline"` Default *float64 `yaml:"default"` } func (p *FloatParameter) Parse(v any) (any, error) { var out float64 switch newV := v.(type) { default: return nil, &ParseTypeError{p.Name, p.Type, v} case float32: out = float64(newV) case float64: out = newV case json.Number: newI, err := newV.Float64() if err != nil { return nil, &ParseTypeError{p.Name, p.Type, v} } out = float64(newI) } return out, nil } func (p *FloatParameter) GetAuthServices() []ParamAuthService { return p.AuthServices } func (p *FloatParameter) GetDefault() any { if p.Default == nil { return nil } return *p.Default } // Manifest returns the manifest for the FloatParameter. func (p *FloatParameter) Manifest() ParameterManifest { // only list ParamAuthService names (without fields) in manifest authServiceNames := getAuthServiceNames(p.AuthServices) r := CheckParamRequired(p.GetRequired(), p.GetDefault()) return ParameterManifest{ Name: p.Name, Type: p.Type, Required: r, Description: p.Desc, AuthServices: authServiceNames, } } // McpManifest returns the MCP manifest for the FloatParameter. // json schema only allow numeric types of 'integer' and 'number'. func (p *FloatParameter) McpManifest() (ParameterMcpManifest, []string) { authServiceNames := getAuthServiceNames(p.AuthServices) return ParameterMcpManifest{ Type: "number", Description: p.Desc, }, authServiceNames } // NewBooleanParameter is a convenience function for initializing a BooleanParameter. func NewBooleanParameter(name string, desc string) *BooleanParameter { return &BooleanParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeBool, Desc: desc, AuthServices: nil, }, } } // NewBooleanParameterWithDefault is a convenience function for initializing a BooleanParameter with default value. func NewBooleanParameterWithDefault(name string, defaultV bool, desc string) *BooleanParameter { return &BooleanParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeBool, Desc: desc, AuthServices: nil, }, Default: &defaultV, } } // NewBooleanParameterWithRequired is a convenience function for initializing a BooleanParameter. func NewBooleanParameterWithRequired(name string, desc string, required bool) *BooleanParameter { return &BooleanParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeBool, Desc: desc, Required: &required, AuthServices: nil, }, } } // NewBooleanParameterWithAuth is a convenience function for initializing a BooleanParameter with a list of ParamAuthService. func NewBooleanParameterWithAuth(name string, desc string, authServices []ParamAuthService) *BooleanParameter { return &BooleanParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeBool, Desc: desc, AuthServices: authServices, }, } } var _ Parameter = &BooleanParameter{} // BooleanParameter is a parameter representing the "boolean" type. type BooleanParameter struct { CommonParameter `yaml:",inline"` Default *bool `yaml:"default"` } func (p *BooleanParameter) Parse(v any) (any, error) { newV, ok := v.(bool) if !ok { return nil, &ParseTypeError{p.Name, p.Type, v} } return newV, nil } func (p *BooleanParameter) GetAuthServices() []ParamAuthService { return p.AuthServices } func (p *BooleanParameter) GetDefault() any { if p.Default == nil { return nil } return *p.Default } // Manifest returns the manifest for the BooleanParameter. func (p *BooleanParameter) Manifest() ParameterManifest { // only list ParamAuthService names (without fields) in manifest authServiceNames := getAuthServiceNames(p.AuthServices) r := CheckParamRequired(p.GetRequired(), p.GetDefault()) return ParameterManifest{ Name: p.Name, Type: p.Type, Required: r, Description: p.Desc, AuthServices: authServiceNames, } } // NewArrayParameter is a convenience function for initializing a ArrayParameter. func NewArrayParameter(name string, desc string, items Parameter) *ArrayParameter { return &ArrayParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeArray, Desc: desc, AuthServices: nil, }, Items: items, } } // NewArrayParameterWithDefault is a convenience function for initializing a ArrayParameter with default value. func NewArrayParameterWithDefault(name string, defaultV []any, desc string, items Parameter) *ArrayParameter { return &ArrayParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeArray, Desc: desc, AuthServices: nil, }, Items: items, Default: &defaultV, } } // NewArrayParameterWithRequired is a convenience function for initializing a ArrayParameter with default value. func NewArrayParameterWithRequired(name string, desc string, required bool, items Parameter) *ArrayParameter { return &ArrayParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeArray, Desc: desc, Required: &required, AuthServices: nil, }, Items: items, } } // NewArrayParameterWithAuth is a convenience function for initializing a ArrayParameter with a list of ParamAuthService. func NewArrayParameterWithAuth(name string, desc string, items Parameter, authServices []ParamAuthService) *ArrayParameter { return &ArrayParameter{ CommonParameter: CommonParameter{ Name: name, Type: typeArray, Desc: desc, AuthServices: authServices, }, Items: items, } } var _ Parameter = &ArrayParameter{} // ArrayParameter is a parameter representing the "array" type. type ArrayParameter struct { CommonParameter `yaml:",inline"` Default *[]any `yaml:"default"` Items Parameter `yaml:"items"` } func (p *ArrayParameter) UnmarshalYAML(ctx context.Context, unmarshal func(interface{}) error) error { var rawItem struct { CommonParameter `yaml:",inline"` Default *[]any `yaml:"default"` Items util.DelayedUnmarshaler `yaml:"items"` } if err := unmarshal(&rawItem); err != nil { return err } p.CommonParameter = rawItem.CommonParameter p.Default = rawItem.Default i, err := parseParamFromDelayedUnmarshaler(ctx, &rawItem.Items) if err != nil { return fmt.Errorf("unable to parse 'items' field: %w", err) } if i.GetAuthServices() != nil && len(i.GetAuthServices()) != 0 { return fmt.Errorf("nested items should not have auth services") } p.Items = i return nil } func (p *ArrayParameter) Parse(v any) (any, error) { arrVal, ok := v.([]any) if !ok { return nil, &ParseTypeError{p.Name, p.Type, arrVal} } rtn := make([]any, 0, len(arrVal)) for idx, val := range arrVal { val, err := p.Items.Parse(val) if err != nil { return nil, fmt.Errorf("unable to parse element #%d: %w", idx, err) } rtn = append(rtn, val) } return rtn, nil } func (p *ArrayParameter) GetAuthServices() []ParamAuthService { return p.AuthServices } func (p *ArrayParameter) GetDefault() any { if p.Default == nil { return nil } return *p.Default } func (p *ArrayParameter) GetItems() Parameter { return p.Items } // Manifest returns the manifest for the ArrayParameter. func (p *ArrayParameter) Manifest() ParameterManifest { // only list ParamAuthService names (without fields) in manifest authServiceNames := getAuthServiceNames(p.AuthServices) items := p.Items.Manifest() // if required value is true, or there's no default value r := CheckParamRequired(p.GetRequired(), p.GetDefault()) items.Required = r return ParameterManifest{ Name: p.Name, Type: p.Type, Required: r, Description: p.Desc, AuthServices: authServiceNames, Items: &items, } } // McpManifest returns the MCP manifest for the ArrayParameter. func (p *ArrayParameter) McpManifest() (ParameterMcpManifest, []string) { // only list ParamAuthService names (without fields) in manifest authServiceNames := getAuthServiceNames(p.AuthServices) items, _ := p.Items.McpManifest() return ParameterMcpManifest{ Type: p.Type, Description: p.Desc, Items: &items, }, authServiceNames } // MapParameter is a parameter representing a map with string keys. If ValueType is // specified (e.g., "string"), values are validated against that type. If ValueType // is empty, it is treated as a generic map[string]any. type MapParameter struct { CommonParameter `yaml:",inline"` Default *map[string]any `yaml:"default,omitempty"` ValueType string `yaml:"valueType,omitempty"` } // Ensure MapParameter implements the Parameter interface. var _ Parameter = &MapParameter{} // NewMapParameter is a convenience function for initializing a MapParameter. func NewMapParameter(name string, desc string, valueType string) *MapParameter { return &MapParameter{ CommonParameter: CommonParameter{ Name: name, Type: "map", Desc: desc, }, ValueType: valueType, } } // NewMapParameterWithDefault is a convenience function for initializing a MapParameter with a default value. func NewMapParameterWithDefault(name string, defaultV map[string]any, desc string, valueType string) *MapParameter { return &MapParameter{ CommonParameter: CommonParameter{ Name: name, Type: "map", Desc: desc, }, ValueType: valueType, Default: &defaultV, } } // NewMapParameterWithRequired is a convenience function for initializing a MapParameter as required. func NewMapParameterWithRequired(name string, desc string, required bool, valueType string) *MapParameter { return &MapParameter{ CommonParameter: CommonParameter{ Name: name, Type: "map", Desc: desc, Required: &required, }, ValueType: valueType, } } // NewMapParameterWithAuth is a convenience function for initializing a MapParameter with auth services. func NewMapParameterWithAuth(name string, desc string, valueType string, authServices []ParamAuthService) *MapParameter { return &MapParameter{ CommonParameter: CommonParameter{ Name: name, Type: "map", Desc: desc, AuthServices: authServices, }, ValueType: valueType, } } // UnmarshalYAML handles parsing the MapParameter from YAML input. func (p *MapParameter) UnmarshalYAML(ctx context.Context, unmarshal func(interface{}) error) error { var rawItem struct { CommonParameter `yaml:",inline"` Default *map[string]any `yaml:"default"` ValueType string `yaml:"valueType"` } if err := unmarshal(&rawItem); err != nil { return err } // Validate `ValueType` to be one of the supported basic types if rawItem.ValueType != "" { if _, err := getPrototypeParameter(rawItem.ValueType); err != nil { return err } } p.CommonParameter = rawItem.CommonParameter p.Default = rawItem.Default p.ValueType = rawItem.ValueType return nil } // getPrototypeParameter is a helper factory to create a temporary parameter // based on a type string for parsing and manifest generation. func getPrototypeParameter(typeName string) (Parameter, error) { switch typeName { case "string": return NewStringParameter("", ""), nil case "integer": return NewIntParameter("", ""), nil case "boolean": return NewBooleanParameter("", ""), nil case "float": return NewFloatParameter("", ""), nil default: return nil, fmt.Errorf("unsupported valueType %q for map parameter", typeName) } } // Parse validates and parses an incoming value for the map parameter. func (p *MapParameter) Parse(v any) (any, error) { m, ok := v.(map[string]any) if !ok { return nil, &ParseTypeError{p.Name, p.Type, m} } // for generic maps, convert json.Numbers to their corresponding types if p.ValueType == "" { convertedData, err := util.ConvertNumbers(m) if err != nil { return nil, fmt.Errorf("failed to parse integer or float values in map: %s", err) } convertedMap, ok := convertedData.(map[string]any) if !ok { return nil, fmt.Errorf("internal error: ConvertNumbers should return a map, but got type %T", convertedData) } return convertedMap, nil } // Otherwise, get a prototype and parse each value in the map. prototype, err := getPrototypeParameter(p.ValueType) if err != nil { return nil, err } rtn := make(map[string]any, len(m)) for key, val := range m { parsedVal, err := prototype.Parse(val) if err != nil { return nil, fmt.Errorf("unable to parse value for key %q: %w", key, err) } rtn[key] = parsedVal } return rtn, nil } func (p *MapParameter) GetAuthServices() []ParamAuthService { return p.AuthServices } func (p *MapParameter) GetDefault() any { if p.Default == nil { return nil } return *p.Default } func (p *MapParameter) GetValueType() string { return p.ValueType } // Manifest returns the manifest for the MapParameter. func (p *MapParameter) Manifest() ParameterManifest { authServiceNames := getAuthServiceNames(p.AuthServices) r := CheckParamRequired(p.GetRequired(), p.GetDefault()) var additionalProperties any if p.ValueType != "" { _, err := getPrototypeParameter(p.ValueType) if err != nil { panic(err) } valueSchema := map[string]any{"type": p.ValueType} additionalProperties = valueSchema } else { // If no valueType is given, allow any properties. additionalProperties = true } return ParameterManifest{ Name: p.Name, Type: "object", Required: r, Description: p.Desc, AuthServices: authServiceNames, AdditionalProperties: additionalProperties, } } // McpManifest returns the MCP manifest for the MapParameter. func (p *MapParameter) McpManifest() (ParameterMcpManifest, []string) { authServiceNames := getAuthServiceNames(p.AuthServices) var additionalProperties any if p.ValueType != "" { _, err := getPrototypeParameter(p.ValueType) if err != nil { panic(err) } valueSchema := map[string]any{"type": p.ValueType} additionalProperties = valueSchema } else { // If no valueType is given, allow any properties. additionalProperties = true } return ParameterMcpManifest{ Type: "object", Description: p.Desc, AdditionalProperties: additionalProperties, }, authServiceNames }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/googleapis/genai-toolbox'

If you have feedback or need assistance with the MCP directory API, please join our Discord server