#
tokens: 46543/50000 19/98 files (page 2/2)
lines: off (toggle) GitHub
raw markdown copy
This is page 2 of 2. Use http://codebase.md/metoro-io/metoro-mcp-server?page={x} to view the full context.

# Directory Structure

```
├── .github
│   └── workflows
│       ├── go-test.yml
│       └── release.yml
├── .gitignore
├── .goreleaser.yml
├── go.mod
├── go.sum
├── images
│   └── Metoro_square.svg
├── LICENSE
├── main.go
├── model
│   ├── model_action_email_destination.go
│   ├── model_action_pager_duty_destination.go
│   ├── model_action_slack_destination.go
│   ├── model_action_webhook_destination.go
│   ├── model_action.go
│   ├── model_alert_type.go
│   ├── model_alert.go
│   ├── model_condition_type.go
│   ├── model_condition.go
│   ├── model_create_update_alert_request.go
│   ├── model_expression_config_filters_inner.go
│   ├── model_expression_config_metoro_timeseries_definition.go
│   ├── model_expression_config.go
│   ├── model_metadata_object.go
│   ├── model_metoro_ql_timeseries.go
│   ├── model_operator_config.go
│   ├── model_operator_type.go
│   ├── model_persistence_settings.go
│   ├── model_static_condition.go
│   ├── model_timeseries_config_expression.go
│   ├── model_timeseries_config.go
│   ├── model_timeseries_specifier_filters_inner.go
│   ├── model_timeseries_specifier_function.go
│   ├── model_timeseries_specifier_functions_math_expression.go
│   ├── model_timeseries_specifier_functions.go
│   ├── model_timeseries_specifier_kubernetes_resource.go
│   ├── model_timeseries_specifier_logs.go
│   ├── model_timeseries_specifier_metric.go
│   ├── model_timeseries_specifier_traces.go
│   ├── model_timeseries_specifier.go
│   ├── model.go
│   └── utils.go
├── README.md
├── resources
│   ├── environments.go
│   ├── k8s_events_attributes.go
│   ├── log_attributes.go
│   ├── metrics.go
│   ├── namespaces.go
│   ├── nodes.go
│   ├── resources.go
│   ├── services.go
│   └── trace_attributes.go
├── tools
│   ├── create_ai_issue.go
│   ├── create_alert.go
│   ├── create_dashboard.go
│   ├── create_investigation.go
│   ├── get_ai_issue.go
│   ├── get_alert_fires.go
│   ├── get_alerts.go
│   ├── get_attribute_keys.go
│   ├── get_attribute_values.go
│   ├── get_environments.go
│   ├── get_k8s_event_attribute_values.go
│   ├── get_k8s_events_attributes.go
│   ├── get_k8s_events_volume.go
│   ├── get_k8s_events.go
│   ├── get_k8s_service_information.go
│   ├── get_log_attribute_values.go
│   ├── get_log_attributes.go
│   ├── get_logs.go
│   ├── get_metric_attributes.go
│   ├── get_metric_metadata.go
│   ├── get_metric_names.go
│   ├── get_multi_metric.go
│   ├── get_namespaces.go
│   ├── get_node_attributes.go
│   ├── get_node_info.go
│   ├── get_nodes.go
│   ├── get_pod_by_ip.go
│   ├── get_pods.go
│   ├── get_profiles.go
│   ├── get_service_graph.go
│   ├── get_service_summaries.go
│   ├── get_services.go
│   ├── get_source_repository.go
│   ├── get_trace_attribute_values.go
│   ├── get_trace_attributes.go
│   ├── get_trace_metric.go
│   ├── get_trace_spans.go
│   ├── get_traces_distribution.go
│   ├── get_traces.go
│   ├── get_version_for_service.go
│   ├── list_ai_issue_events.go
│   ├── list_ai_issues.go
│   ├── list_investigations.go
│   ├── tools.go
│   ├── unix_to_rfc3339.go
│   ├── update_ai_issue.go
│   └── update_investigation.go
└── utils
    ├── request_utils.go
    ├── time_utils_test.go
    └── time_utils.go
```

# Files

--------------------------------------------------------------------------------
/model/model_timeseries_specifier_functions.go:
--------------------------------------------------------------------------------

```go
/*
Metoro Alerts API

API for managing alerts in the Metoro observability platform.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"encoding/json"
)

// checks if the TimeseriesSpecifierFunctions type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &TimeseriesSpecifierFunctions{}

// TimeseriesSpecifierFunctions Functions to apply to the timeseries
type TimeseriesSpecifierFunctions struct {
	// Type of function to apply
	FunctionType *string `json:"functionType,omitempty"`
	MathExpression *TimeseriesSpecifierFunctionsMathExpression `json:"mathExpression,omitempty"`
}

// NewTimeseriesSpecifierFunctions instantiates a new TimeseriesSpecifierFunctions object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewTimeseriesSpecifierFunctions() *TimeseriesSpecifierFunctions {
	this := TimeseriesSpecifierFunctions{}
	return &this
}

// NewTimeseriesSpecifierFunctionsWithDefaults instantiates a new TimeseriesSpecifierFunctions object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewTimeseriesSpecifierFunctionsWithDefaults() *TimeseriesSpecifierFunctions {
	this := TimeseriesSpecifierFunctions{}
	return &this
}

// GetFunctionType returns the FunctionType field value if set, zero value otherwise.
func (o *TimeseriesSpecifierFunctions) GetFunctionType() string {
	if o == nil || IsNil(o.FunctionType) {
		var ret string
		return ret
	}
	return *o.FunctionType
}

// GetFunctionTypeOk returns a tuple with the FunctionType field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifierFunctions) GetFunctionTypeOk() (*string, bool) {
	if o == nil || IsNil(o.FunctionType) {
		return nil, false
	}
	return o.FunctionType, true
}

// HasFunctionType returns a boolean if a field has been set.
func (o *TimeseriesSpecifierFunctions) HasFunctionType() bool {
	if o != nil && !IsNil(o.FunctionType) {
		return true
	}

	return false
}

// SetFunctionType gets a reference to the given string and assigns it to the FunctionType field.
func (o *TimeseriesSpecifierFunctions) SetFunctionType(v string) {
	o.FunctionType = &v
}

// GetMathExpression returns the MathExpression field value if set, zero value otherwise.
func (o *TimeseriesSpecifierFunctions) GetMathExpression() TimeseriesSpecifierFunctionsMathExpression {
	if o == nil || IsNil(o.MathExpression) {
		var ret TimeseriesSpecifierFunctionsMathExpression
		return ret
	}
	return *o.MathExpression
}

// GetMathExpressionOk returns a tuple with the MathExpression field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifierFunctions) GetMathExpressionOk() (*TimeseriesSpecifierFunctionsMathExpression, bool) {
	if o == nil || IsNil(o.MathExpression) {
		return nil, false
	}
	return o.MathExpression, true
}

// HasMathExpression returns a boolean if a field has been set.
func (o *TimeseriesSpecifierFunctions) HasMathExpression() bool {
	if o != nil && !IsNil(o.MathExpression) {
		return true
	}

	return false
}

// SetMathExpression gets a reference to the given TimeseriesSpecifierFunctionsMathExpression and assigns it to the MathExpression field.
func (o *TimeseriesSpecifierFunctions) SetMathExpression(v TimeseriesSpecifierFunctionsMathExpression) {
	o.MathExpression = &v
}

func (o TimeseriesSpecifierFunctions) MarshalJSON() ([]byte, error) {
	toSerialize,err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o TimeseriesSpecifierFunctions) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.FunctionType) {
		toSerialize["functionType"] = o.FunctionType
	}
	if !IsNil(o.MathExpression) {
		toSerialize["mathExpression"] = o.MathExpression
	}
	return toSerialize, nil
}

type NullableTimeseriesSpecifierFunctions struct {
	value *TimeseriesSpecifierFunctions
	isSet bool
}

func (v NullableTimeseriesSpecifierFunctions) Get() *TimeseriesSpecifierFunctions {
	return v.value
}

func (v *NullableTimeseriesSpecifierFunctions) Set(val *TimeseriesSpecifierFunctions) {
	v.value = val
	v.isSet = true
}

func (v NullableTimeseriesSpecifierFunctions) IsSet() bool {
	return v.isSet
}

func (v *NullableTimeseriesSpecifierFunctions) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableTimeseriesSpecifierFunctions(val *TimeseriesSpecifierFunctions) *NullableTimeseriesSpecifierFunctions {
	return &NullableTimeseriesSpecifierFunctions{value: val, isSet: true}
}

func (v NullableTimeseriesSpecifierFunctions) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableTimeseriesSpecifierFunctions) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}



```

--------------------------------------------------------------------------------
/model/model_timeseries_specifier_functions_math_expression.go:
--------------------------------------------------------------------------------

```go
/*
Metoro Alerts API

API for managing alerts in the Metoro observability platform.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"encoding/json"
)

// checks if the TimeseriesSpecifierFunctionsMathExpression type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &TimeseriesSpecifierFunctionsMathExpression{}

// TimeseriesSpecifierFunctionsMathExpression Mathematical expression for custom functions
type TimeseriesSpecifierFunctionsMathExpression struct {
	// Array of variable names used in the expression
	Variables []string `json:"variables,omitempty"`
	// The mathematical expression to apply
	Expression *string `json:"expression,omitempty"`
}

// NewTimeseriesSpecifierFunctionsMathExpression instantiates a new TimeseriesSpecifierFunctionsMathExpression object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewTimeseriesSpecifierFunctionsMathExpression() *TimeseriesSpecifierFunctionsMathExpression {
	this := TimeseriesSpecifierFunctionsMathExpression{}
	return &this
}

// NewTimeseriesSpecifierFunctionsMathExpressionWithDefaults instantiates a new TimeseriesSpecifierFunctionsMathExpression object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewTimeseriesSpecifierFunctionsMathExpressionWithDefaults() *TimeseriesSpecifierFunctionsMathExpression {
	this := TimeseriesSpecifierFunctionsMathExpression{}
	return &this
}

// GetVariables returns the Variables field value if set, zero value otherwise.
func (o *TimeseriesSpecifierFunctionsMathExpression) GetVariables() []string {
	if o == nil || IsNil(o.Variables) {
		var ret []string
		return ret
	}
	return o.Variables
}

// GetVariablesOk returns a tuple with the Variables field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifierFunctionsMathExpression) GetVariablesOk() ([]string, bool) {
	if o == nil || IsNil(o.Variables) {
		return nil, false
	}
	return o.Variables, true
}

// HasVariables returns a boolean if a field has been set.
func (o *TimeseriesSpecifierFunctionsMathExpression) HasVariables() bool {
	if o != nil && !IsNil(o.Variables) {
		return true
	}

	return false
}

// SetVariables gets a reference to the given []string and assigns it to the Variables field.
func (o *TimeseriesSpecifierFunctionsMathExpression) SetVariables(v []string) {
	o.Variables = v
}

// GetExpression returns the Expression field value if set, zero value otherwise.
func (o *TimeseriesSpecifierFunctionsMathExpression) GetExpression() string {
	if o == nil || IsNil(o.Expression) {
		var ret string
		return ret
	}
	return *o.Expression
}

// GetExpressionOk returns a tuple with the Expression field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifierFunctionsMathExpression) GetExpressionOk() (*string, bool) {
	if o == nil || IsNil(o.Expression) {
		return nil, false
	}
	return o.Expression, true
}

// HasExpression returns a boolean if a field has been set.
func (o *TimeseriesSpecifierFunctionsMathExpression) HasExpression() bool {
	if o != nil && !IsNil(o.Expression) {
		return true
	}

	return false
}

// SetExpression gets a reference to the given string and assigns it to the Expression field.
func (o *TimeseriesSpecifierFunctionsMathExpression) SetExpression(v string) {
	o.Expression = &v
}

func (o TimeseriesSpecifierFunctionsMathExpression) MarshalJSON() ([]byte, error) {
	toSerialize,err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o TimeseriesSpecifierFunctionsMathExpression) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.Variables) {
		toSerialize["variables"] = o.Variables
	}
	if !IsNil(o.Expression) {
		toSerialize["expression"] = o.Expression
	}
	return toSerialize, nil
}

type NullableTimeseriesSpecifierFunctionsMathExpression struct {
	value *TimeseriesSpecifierFunctionsMathExpression
	isSet bool
}

func (v NullableTimeseriesSpecifierFunctionsMathExpression) Get() *TimeseriesSpecifierFunctionsMathExpression {
	return v.value
}

func (v *NullableTimeseriesSpecifierFunctionsMathExpression) Set(val *TimeseriesSpecifierFunctionsMathExpression) {
	v.value = val
	v.isSet = true
}

func (v NullableTimeseriesSpecifierFunctionsMathExpression) IsSet() bool {
	return v.isSet
}

func (v *NullableTimeseriesSpecifierFunctionsMathExpression) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableTimeseriesSpecifierFunctionsMathExpression(val *TimeseriesSpecifierFunctionsMathExpression) *NullableTimeseriesSpecifierFunctionsMathExpression {
	return &NullableTimeseriesSpecifierFunctionsMathExpression{value: val, isSet: true}
}

func (v NullableTimeseriesSpecifierFunctionsMathExpression) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableTimeseriesSpecifierFunctionsMathExpression) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}



```

--------------------------------------------------------------------------------
/model/model_metadata_object.go:
--------------------------------------------------------------------------------

```go
/*
Metoro API

API for managing Metoro environments, alerts, and dashboards.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"bytes"
	"encoding/json"
	"fmt"
)

// checks if the MetadataObject type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &MetadataObject{}

// MetadataObject struct for MetadataObject
type MetadataObject struct {
	// Name of the alert
	Name string `json:"name"`
	// Description of the alert with additional context
	Description *string `json:"description,omitempty"`
	// Unique identifier for the alert.
	Id string `json:"id"`
}

type _MetadataObject MetadataObject

// NewMetadataObject instantiates a new MetadataObject object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewMetadataObject(name string, id string) *MetadataObject {
	this := MetadataObject{}
	this.Name = name
	this.Id = id
	return &this
}

// NewMetadataObjectWithDefaults instantiates a new MetadataObject object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewMetadataObjectWithDefaults() *MetadataObject {
	this := MetadataObject{}
	return &this
}

// GetName returns the Name field value
func (o *MetadataObject) GetName() string {
	if o == nil {
		var ret string
		return ret
	}

	return o.Name
}

// GetNameOk returns a tuple with the Name field value
// and a boolean to check if the value has been set.
func (o *MetadataObject) GetNameOk() (*string, bool) {
	if o == nil {
		return nil, false
	}
	return &o.Name, true
}

// SetName sets field value
func (o *MetadataObject) SetName(v string) {
	o.Name = v
}

// GetDescription returns the Description field value if set, zero value otherwise.
func (o *MetadataObject) GetDescription() string {
	if o == nil || IsNil(o.Description) {
		var ret string
		return ret
	}
	return *o.Description
}

// GetDescriptionOk returns a tuple with the Description field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *MetadataObject) GetDescriptionOk() (*string, bool) {
	if o == nil || IsNil(o.Description) {
		return nil, false
	}
	return o.Description, true
}

// HasDescription returns a boolean if a field has been set.
func (o *MetadataObject) HasDescription() bool {
	if o != nil && !IsNil(o.Description) {
		return true
	}

	return false
}

// SetDescription gets a reference to the given string and assigns it to the Description field.
func (o *MetadataObject) SetDescription(v string) {
	o.Description = &v
}

// GetId returns the Id field value
func (o *MetadataObject) GetId() string {
	if o == nil {
		var ret string
		return ret
	}

	return o.Id
}

// GetIdOk returns a tuple with the Id field value
// and a boolean to check if the value has been set.
func (o *MetadataObject) GetIdOk() (*string, bool) {
	if o == nil {
		return nil, false
	}
	return &o.Id, true
}

// SetId sets field value
func (o *MetadataObject) SetId(v string) {
	o.Id = v
}

func (o MetadataObject) MarshalJSON() ([]byte, error) {
	toSerialize, err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o MetadataObject) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["name"] = o.Name
	if !IsNil(o.Description) {
		toSerialize["description"] = o.Description
	}
	toSerialize["id"] = o.Id
	return toSerialize, nil
}

func (o *MetadataObject) UnmarshalJSON(data []byte) (err error) {
	// This validates that all required properties are included in the JSON object
	// by unmarshalling the object into a generic map with string keys and checking
	// that every required field exists as a key in the generic map.
	requiredProperties := []string{
		"name",
		"id",
	}

	allProperties := make(map[string]interface{})

	err = json.Unmarshal(data, &allProperties)

	if err != nil {
		return err
	}

	for _, requiredProperty := range requiredProperties {
		if _, exists := allProperties[requiredProperty]; !exists {
			return fmt.Errorf("no value given for required property %v", requiredProperty)
		}
	}

	varMetadataObject := _MetadataObject{}

	decoder := json.NewDecoder(bytes.NewReader(data))
	decoder.DisallowUnknownFields()
	err = decoder.Decode(&varMetadataObject)

	if err != nil {
		return err
	}

	*o = MetadataObject(varMetadataObject)

	return err
}

type NullableMetadataObject struct {
	value *MetadataObject
	isSet bool
}

func (v NullableMetadataObject) Get() *MetadataObject {
	return v.value
}

func (v *NullableMetadataObject) Set(val *MetadataObject) {
	v.value = val
	v.isSet = true
}

func (v NullableMetadataObject) IsSet() bool {
	return v.isSet
}

func (v *NullableMetadataObject) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableMetadataObject(val *MetadataObject) *NullableMetadataObject {
	return &NullableMetadataObject{value: val, isSet: true}
}

func (v NullableMetadataObject) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableMetadataObject) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

```

--------------------------------------------------------------------------------
/model/model_expression_config_filters_inner.go:
--------------------------------------------------------------------------------

```go
/*
Metoro Alerts API

API for managing alerts in the Metoro observability platform.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"encoding/json"
)

// checks if the ExpressionConfigFiltersInner type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &ExpressionConfigFiltersInner{}

// ExpressionConfigFiltersInner struct for ExpressionConfigFiltersInner
type ExpressionConfigFiltersInner struct {
	Key *string `json:"key,omitempty"`
	Value []string `json:"value,omitempty"`
	Operator *string `json:"operator,omitempty"`
}

// NewExpressionConfigFiltersInner instantiates a new ExpressionConfigFiltersInner object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewExpressionConfigFiltersInner() *ExpressionConfigFiltersInner {
	this := ExpressionConfigFiltersInner{}
	return &this
}

// NewExpressionConfigFiltersInnerWithDefaults instantiates a new ExpressionConfigFiltersInner object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewExpressionConfigFiltersInnerWithDefaults() *ExpressionConfigFiltersInner {
	this := ExpressionConfigFiltersInner{}
	return &this
}

// GetKey returns the Key field value if set, zero value otherwise.
func (o *ExpressionConfigFiltersInner) GetKey() string {
	if o == nil || IsNil(o.Key) {
		var ret string
		return ret
	}
	return *o.Key
}

// GetKeyOk returns a tuple with the Key field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ExpressionConfigFiltersInner) GetKeyOk() (*string, bool) {
	if o == nil || IsNil(o.Key) {
		return nil, false
	}
	return o.Key, true
}

// HasKey returns a boolean if a field has been set.
func (o *ExpressionConfigFiltersInner) HasKey() bool {
	if o != nil && !IsNil(o.Key) {
		return true
	}

	return false
}

// SetKey gets a reference to the given string and assigns it to the Key field.
func (o *ExpressionConfigFiltersInner) SetKey(v string) {
	o.Key = &v
}

// GetValue returns the Value field value if set, zero value otherwise.
func (o *ExpressionConfigFiltersInner) GetValue() []string {
	if o == nil || IsNil(o.Value) {
		var ret []string
		return ret
	}
	return o.Value
}

// GetValueOk returns a tuple with the Value field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ExpressionConfigFiltersInner) GetValueOk() ([]string, bool) {
	if o == nil || IsNil(o.Value) {
		return nil, false
	}
	return o.Value, true
}

// HasValue returns a boolean if a field has been set.
func (o *ExpressionConfigFiltersInner) HasValue() bool {
	if o != nil && !IsNil(o.Value) {
		return true
	}

	return false
}

// SetValue gets a reference to the given []string and assigns it to the Value field.
func (o *ExpressionConfigFiltersInner) SetValue(v []string) {
	o.Value = v
}

// GetOperator returns the Operator field value if set, zero value otherwise.
func (o *ExpressionConfigFiltersInner) GetOperator() string {
	if o == nil || IsNil(o.Operator) {
		var ret string
		return ret
	}
	return *o.Operator
}

// GetOperatorOk returns a tuple with the Operator field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ExpressionConfigFiltersInner) GetOperatorOk() (*string, bool) {
	if o == nil || IsNil(o.Operator) {
		return nil, false
	}
	return o.Operator, true
}

// HasOperator returns a boolean if a field has been set.
func (o *ExpressionConfigFiltersInner) HasOperator() bool {
	if o != nil && !IsNil(o.Operator) {
		return true
	}

	return false
}

// SetOperator gets a reference to the given string and assigns it to the Operator field.
func (o *ExpressionConfigFiltersInner) SetOperator(v string) {
	o.Operator = &v
}

func (o ExpressionConfigFiltersInner) MarshalJSON() ([]byte, error) {
	toSerialize,err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o ExpressionConfigFiltersInner) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.Key) {
		toSerialize["key"] = o.Key
	}
	if !IsNil(o.Value) {
		toSerialize["value"] = o.Value
	}
	if !IsNil(o.Operator) {
		toSerialize["operator"] = o.Operator
	}
	return toSerialize, nil
}

type NullableExpressionConfigFiltersInner struct {
	value *ExpressionConfigFiltersInner
	isSet bool
}

func (v NullableExpressionConfigFiltersInner) Get() *ExpressionConfigFiltersInner {
	return v.value
}

func (v *NullableExpressionConfigFiltersInner) Set(val *ExpressionConfigFiltersInner) {
	v.value = val
	v.isSet = true
}

func (v NullableExpressionConfigFiltersInner) IsSet() bool {
	return v.isSet
}

func (v *NullableExpressionConfigFiltersInner) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableExpressionConfigFiltersInner(val *ExpressionConfigFiltersInner) *NullableExpressionConfigFiltersInner {
	return &NullableExpressionConfigFiltersInner{value: val, isSet: true}
}

func (v NullableExpressionConfigFiltersInner) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableExpressionConfigFiltersInner) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}



```

--------------------------------------------------------------------------------
/model/model_expression_config_metoro_timeseries_definition.go:
--------------------------------------------------------------------------------

```go
/*
Metoro Alerts API

API for managing alerts in the Metoro observability platform.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"encoding/json"
)

// checks if the ExpressionConfigMetoroTimeseriesDefinition type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &ExpressionConfigMetoroTimeseriesDefinition{}

// ExpressionConfigMetoroTimeseriesDefinition Metoro-specific timeseries definition
type ExpressionConfigMetoroTimeseriesDefinition struct {
	// List of timeseries specifiers
	TimeseriesSpecifiers []TimeseriesSpecifier `json:"timeseriesSpecifiers,omitempty"`
	// A formula to combine multiple timeseries
	Formula *string `json:"formula,omitempty"`
}

// NewExpressionConfigMetoroTimeseriesDefinition instantiates a new ExpressionConfigMetoroTimeseriesDefinition object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewExpressionConfigMetoroTimeseriesDefinition() *ExpressionConfigMetoroTimeseriesDefinition {
	this := ExpressionConfigMetoroTimeseriesDefinition{}
	return &this
}

// NewExpressionConfigMetoroTimeseriesDefinitionWithDefaults instantiates a new ExpressionConfigMetoroTimeseriesDefinition object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewExpressionConfigMetoroTimeseriesDefinitionWithDefaults() *ExpressionConfigMetoroTimeseriesDefinition {
	this := ExpressionConfigMetoroTimeseriesDefinition{}
	return &this
}

// GetTimeseriesSpecifiers returns the TimeseriesSpecifiers field value if set, zero value otherwise.
func (o *ExpressionConfigMetoroTimeseriesDefinition) GetTimeseriesSpecifiers() []TimeseriesSpecifier {
	if o == nil || IsNil(o.TimeseriesSpecifiers) {
		var ret []TimeseriesSpecifier
		return ret
	}
	return o.TimeseriesSpecifiers
}

// GetTimeseriesSpecifiersOk returns a tuple with the TimeseriesSpecifiers field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ExpressionConfigMetoroTimeseriesDefinition) GetTimeseriesSpecifiersOk() ([]TimeseriesSpecifier, bool) {
	if o == nil || IsNil(o.TimeseriesSpecifiers) {
		return nil, false
	}
	return o.TimeseriesSpecifiers, true
}

// HasTimeseriesSpecifiers returns a boolean if a field has been set.
func (o *ExpressionConfigMetoroTimeseriesDefinition) HasTimeseriesSpecifiers() bool {
	if o != nil && !IsNil(o.TimeseriesSpecifiers) {
		return true
	}

	return false
}

// SetTimeseriesSpecifiers gets a reference to the given []TimeseriesSpecifier and assigns it to the TimeseriesSpecifiers field.
func (o *ExpressionConfigMetoroTimeseriesDefinition) SetTimeseriesSpecifiers(v []TimeseriesSpecifier) {
	o.TimeseriesSpecifiers = v
}

// GetFormula returns the Formula field value if set, zero value otherwise.
func (o *ExpressionConfigMetoroTimeseriesDefinition) GetFormula() string {
	if o == nil || IsNil(o.Formula) {
		var ret string
		return ret
	}
	return *o.Formula
}

// GetFormulaOk returns a tuple with the Formula field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ExpressionConfigMetoroTimeseriesDefinition) GetFormulaOk() (*string, bool) {
	if o == nil || IsNil(o.Formula) {
		return nil, false
	}
	return o.Formula, true
}

// HasFormula returns a boolean if a field has been set.
func (o *ExpressionConfigMetoroTimeseriesDefinition) HasFormula() bool {
	if o != nil && !IsNil(o.Formula) {
		return true
	}

	return false
}

// SetFormula gets a reference to the given string and assigns it to the Formula field.
func (o *ExpressionConfigMetoroTimeseriesDefinition) SetFormula(v string) {
	o.Formula = &v
}

func (o ExpressionConfigMetoroTimeseriesDefinition) MarshalJSON() ([]byte, error) {
	toSerialize,err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o ExpressionConfigMetoroTimeseriesDefinition) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.TimeseriesSpecifiers) {
		toSerialize["timeseriesSpecifiers"] = o.TimeseriesSpecifiers
	}
	if !IsNil(o.Formula) {
		toSerialize["formula"] = o.Formula
	}
	return toSerialize, nil
}

type NullableExpressionConfigMetoroTimeseriesDefinition struct {
	value *ExpressionConfigMetoroTimeseriesDefinition
	isSet bool
}

func (v NullableExpressionConfigMetoroTimeseriesDefinition) Get() *ExpressionConfigMetoroTimeseriesDefinition {
	return v.value
}

func (v *NullableExpressionConfigMetoroTimeseriesDefinition) Set(val *ExpressionConfigMetoroTimeseriesDefinition) {
	v.value = val
	v.isSet = true
}

func (v NullableExpressionConfigMetoroTimeseriesDefinition) IsSet() bool {
	return v.isSet
}

func (v *NullableExpressionConfigMetoroTimeseriesDefinition) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableExpressionConfigMetoroTimeseriesDefinition(val *ExpressionConfigMetoroTimeseriesDefinition) *NullableExpressionConfigMetoroTimeseriesDefinition {
	return &NullableExpressionConfigMetoroTimeseriesDefinition{value: val, isSet: true}
}

func (v NullableExpressionConfigMetoroTimeseriesDefinition) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableExpressionConfigMetoroTimeseriesDefinition) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}



```

--------------------------------------------------------------------------------
/model/model_timeseries_specifier_filters_inner.go:
--------------------------------------------------------------------------------

```go
/*
Metoro Alerts API

API for managing alerts in the Metoro observability platform.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"encoding/json"
)

// checks if the TimeseriesSpecifierFiltersInner type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &TimeseriesSpecifierFiltersInner{}

// TimeseriesSpecifierFiltersInner struct for TimeseriesSpecifierFiltersInner
type TimeseriesSpecifierFiltersInner struct {
	// Filter key
	Key *string `json:"key,omitempty"`
	// Filter values
	Value []string `json:"value,omitempty"`
	// Filter operator
	Operator *string `json:"operator,omitempty"`
}

// NewTimeseriesSpecifierFiltersInner instantiates a new TimeseriesSpecifierFiltersInner object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewTimeseriesSpecifierFiltersInner() *TimeseriesSpecifierFiltersInner {
	this := TimeseriesSpecifierFiltersInner{}
	return &this
}

// NewTimeseriesSpecifierFiltersInnerWithDefaults instantiates a new TimeseriesSpecifierFiltersInner object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewTimeseriesSpecifierFiltersInnerWithDefaults() *TimeseriesSpecifierFiltersInner {
	this := TimeseriesSpecifierFiltersInner{}
	return &this
}

// GetKey returns the Key field value if set, zero value otherwise.
func (o *TimeseriesSpecifierFiltersInner) GetKey() string {
	if o == nil || IsNil(o.Key) {
		var ret string
		return ret
	}
	return *o.Key
}

// GetKeyOk returns a tuple with the Key field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifierFiltersInner) GetKeyOk() (*string, bool) {
	if o == nil || IsNil(o.Key) {
		return nil, false
	}
	return o.Key, true
}

// HasKey returns a boolean if a field has been set.
func (o *TimeseriesSpecifierFiltersInner) HasKey() bool {
	if o != nil && !IsNil(o.Key) {
		return true
	}

	return false
}

// SetKey gets a reference to the given string and assigns it to the Key field.
func (o *TimeseriesSpecifierFiltersInner) SetKey(v string) {
	o.Key = &v
}

// GetValue returns the Value field value if set, zero value otherwise.
func (o *TimeseriesSpecifierFiltersInner) GetValue() []string {
	if o == nil || IsNil(o.Value) {
		var ret []string
		return ret
	}
	return o.Value
}

// GetValueOk returns a tuple with the Value field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifierFiltersInner) GetValueOk() ([]string, bool) {
	if o == nil || IsNil(o.Value) {
		return nil, false
	}
	return o.Value, true
}

// HasValue returns a boolean if a field has been set.
func (o *TimeseriesSpecifierFiltersInner) HasValue() bool {
	if o != nil && !IsNil(o.Value) {
		return true
	}

	return false
}

// SetValue gets a reference to the given []string and assigns it to the Value field.
func (o *TimeseriesSpecifierFiltersInner) SetValue(v []string) {
	o.Value = v
}

// GetOperator returns the Operator field value if set, zero value otherwise.
func (o *TimeseriesSpecifierFiltersInner) GetOperator() string {
	if o == nil || IsNil(o.Operator) {
		var ret string
		return ret
	}
	return *o.Operator
}

// GetOperatorOk returns a tuple with the Operator field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifierFiltersInner) GetOperatorOk() (*string, bool) {
	if o == nil || IsNil(o.Operator) {
		return nil, false
	}
	return o.Operator, true
}

// HasOperator returns a boolean if a field has been set.
func (o *TimeseriesSpecifierFiltersInner) HasOperator() bool {
	if o != nil && !IsNil(o.Operator) {
		return true
	}

	return false
}

// SetOperator gets a reference to the given string and assigns it to the Operator field.
func (o *TimeseriesSpecifierFiltersInner) SetOperator(v string) {
	o.Operator = &v
}

func (o TimeseriesSpecifierFiltersInner) MarshalJSON() ([]byte, error) {
	toSerialize,err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o TimeseriesSpecifierFiltersInner) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.Key) {
		toSerialize["key"] = o.Key
	}
	if !IsNil(o.Value) {
		toSerialize["value"] = o.Value
	}
	if !IsNil(o.Operator) {
		toSerialize["operator"] = o.Operator
	}
	return toSerialize, nil
}

type NullableTimeseriesSpecifierFiltersInner struct {
	value *TimeseriesSpecifierFiltersInner
	isSet bool
}

func (v NullableTimeseriesSpecifierFiltersInner) Get() *TimeseriesSpecifierFiltersInner {
	return v.value
}

func (v *NullableTimeseriesSpecifierFiltersInner) Set(val *TimeseriesSpecifierFiltersInner) {
	v.value = val
	v.isSet = true
}

func (v NullableTimeseriesSpecifierFiltersInner) IsSet() bool {
	return v.isSet
}

func (v *NullableTimeseriesSpecifierFiltersInner) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableTimeseriesSpecifierFiltersInner(val *TimeseriesSpecifierFiltersInner) *NullableTimeseriesSpecifierFiltersInner {
	return &NullableTimeseriesSpecifierFiltersInner{value: val, isSet: true}
}

func (v NullableTimeseriesSpecifierFiltersInner) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableTimeseriesSpecifierFiltersInner) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}



```

--------------------------------------------------------------------------------
/model/model_timeseries_specifier_function.go:
--------------------------------------------------------------------------------

```go
/*
Metoro Alerts API

API for managing alerts in the Metoro observability platform.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"encoding/json"
	"bytes"
	"fmt"
)

// checks if the TimeseriesSpecifierFunction type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &TimeseriesSpecifierFunction{}

// TimeseriesSpecifierFunction Function to apply to the timeseries
type TimeseriesSpecifierFunction struct {
	// Type of function to apply
	FunctionType string `json:"functionType"`
	MathExpression *TimeseriesSpecifierFunctionsMathExpression `json:"mathExpression,omitempty"`
}

type _TimeseriesSpecifierFunction TimeseriesSpecifierFunction

// NewTimeseriesSpecifierFunction instantiates a new TimeseriesSpecifierFunction object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewTimeseriesSpecifierFunction(functionType string) *TimeseriesSpecifierFunction {
	this := TimeseriesSpecifierFunction{}
	this.FunctionType = functionType
	return &this
}

// NewTimeseriesSpecifierFunctionWithDefaults instantiates a new TimeseriesSpecifierFunction object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewTimeseriesSpecifierFunctionWithDefaults() *TimeseriesSpecifierFunction {
	this := TimeseriesSpecifierFunction{}
	return &this
}

// GetFunctionType returns the FunctionType field value
func (o *TimeseriesSpecifierFunction) GetFunctionType() string {
	if o == nil {
		var ret string
		return ret
	}

	return o.FunctionType
}

// GetFunctionTypeOk returns a tuple with the FunctionType field value
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifierFunction) GetFunctionTypeOk() (*string, bool) {
	if o == nil {
		return nil, false
	}
	return &o.FunctionType, true
}

// SetFunctionType sets field value
func (o *TimeseriesSpecifierFunction) SetFunctionType(v string) {
	o.FunctionType = v
}

// GetMathExpression returns the MathExpression field value if set, zero value otherwise.
func (o *TimeseriesSpecifierFunction) GetMathExpression() TimeseriesSpecifierFunctionsMathExpression {
	if o == nil || IsNil(o.MathExpression) {
		var ret TimeseriesSpecifierFunctionsMathExpression
		return ret
	}
	return *o.MathExpression
}

// GetMathExpressionOk returns a tuple with the MathExpression field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifierFunction) GetMathExpressionOk() (*TimeseriesSpecifierFunctionsMathExpression, bool) {
	if o == nil || IsNil(o.MathExpression) {
		return nil, false
	}
	return o.MathExpression, true
}

// HasMathExpression returns a boolean if a field has been set.
func (o *TimeseriesSpecifierFunction) HasMathExpression() bool {
	if o != nil && !IsNil(o.MathExpression) {
		return true
	}

	return false
}

// SetMathExpression gets a reference to the given TimeseriesSpecifierFunctionsMathExpression and assigns it to the MathExpression field.
func (o *TimeseriesSpecifierFunction) SetMathExpression(v TimeseriesSpecifierFunctionsMathExpression) {
	o.MathExpression = &v
}

func (o TimeseriesSpecifierFunction) MarshalJSON() ([]byte, error) {
	toSerialize,err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o TimeseriesSpecifierFunction) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["functionType"] = o.FunctionType
	if !IsNil(o.MathExpression) {
		toSerialize["mathExpression"] = o.MathExpression
	}
	return toSerialize, nil
}

func (o *TimeseriesSpecifierFunction) UnmarshalJSON(data []byte) (err error) {
	// This validates that all required properties are included in the JSON object
	// by unmarshalling the object into a generic map with string keys and checking
	// that every required field exists as a key in the generic map.
	requiredProperties := []string{
		"functionType",
	}

	allProperties := make(map[string]interface{})

	err = json.Unmarshal(data, &allProperties)

	if err != nil {
		return err;
	}

	for _, requiredProperty := range(requiredProperties) {
		if _, exists := allProperties[requiredProperty]; !exists {
			return fmt.Errorf("no value given for required property %v", requiredProperty)
		}
	}

	varTimeseriesSpecifierFunction := _TimeseriesSpecifierFunction{}

	decoder := json.NewDecoder(bytes.NewReader(data))
	decoder.DisallowUnknownFields()
	err = decoder.Decode(&varTimeseriesSpecifierFunction)

	if err != nil {
		return err
	}

	*o = TimeseriesSpecifierFunction(varTimeseriesSpecifierFunction)

	return err
}

type NullableTimeseriesSpecifierFunction struct {
	value *TimeseriesSpecifierFunction
	isSet bool
}

func (v NullableTimeseriesSpecifierFunction) Get() *TimeseriesSpecifierFunction {
	return v.value
}

func (v *NullableTimeseriesSpecifierFunction) Set(val *TimeseriesSpecifierFunction) {
	v.value = val
	v.isSet = true
}

func (v NullableTimeseriesSpecifierFunction) IsSet() bool {
	return v.isSet
}

func (v *NullableTimeseriesSpecifierFunction) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableTimeseriesSpecifierFunction(val *TimeseriesSpecifierFunction) *NullableTimeseriesSpecifierFunction {
	return &NullableTimeseriesSpecifierFunction{value: val, isSet: true}
}

func (v NullableTimeseriesSpecifierFunction) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableTimeseriesSpecifierFunction) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}



```

--------------------------------------------------------------------------------
/model/model_condition.go:
--------------------------------------------------------------------------------

```go
/*
Metoro API

API for managing Metoro environments, alerts, and dashboards.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"bytes"
	"encoding/json"
	"fmt"
)

// checks if the Condition type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &Condition{}

// Condition struct for Condition
type Condition struct {
	// Name of the condition
	Name   string           `json:"name"`
	Type   *ConditionType   `json:"type,omitempty"`
	Static *StaticCondition `json:"static,omitempty"`
	// Actions to take when condition is met
	Actions []Action `json:"actions,omitempty"`
}

type _Condition Condition

// NewCondition instantiates a new Condition object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewCondition(name string) *Condition {
	this := Condition{}
	this.Name = name
	return &this
}

// NewConditionWithDefaults instantiates a new Condition object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewConditionWithDefaults() *Condition {
	this := Condition{}
	return &this
}

// GetName returns the Name field value
func (o *Condition) GetName() string {
	if o == nil {
		var ret string
		return ret
	}

	return o.Name
}

// GetNameOk returns a tuple with the Name field value
// and a boolean to check if the value has been set.
func (o *Condition) GetNameOk() (*string, bool) {
	if o == nil {
		return nil, false
	}
	return &o.Name, true
}

// SetName sets field value
func (o *Condition) SetName(v string) {
	o.Name = v
}

// GetType returns the Type field value if set, zero value otherwise.
func (o *Condition) GetType() ConditionType {
	if o == nil || IsNil(o.Type) {
		var ret ConditionType
		return ret
	}
	return *o.Type
}

// GetTypeOk returns a tuple with the Type field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Condition) GetTypeOk() (*ConditionType, bool) {
	if o == nil || IsNil(o.Type) {
		return nil, false
	}
	return o.Type, true
}

// HasType returns a boolean if a field has been set.
func (o *Condition) HasType() bool {
	if o != nil && !IsNil(o.Type) {
		return true
	}

	return false
}

// SetType gets a reference to the given ConditionType and assigns it to the Type field.
func (o *Condition) SetType(v ConditionType) {
	o.Type = &v
}

// GetStatic returns the Static field value if set, zero value otherwise.
func (o *Condition) GetStatic() StaticCondition {
	if o == nil || IsNil(o.Static) {
		var ret StaticCondition
		return ret
	}
	return *o.Static
}

// GetStaticOk returns a tuple with the Static field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Condition) GetStaticOk() (*StaticCondition, bool) {
	if o == nil || IsNil(o.Static) {
		return nil, false
	}
	return o.Static, true
}

// HasStatic returns a boolean if a field has been set.
func (o *Condition) HasStatic() bool {
	if o != nil && !IsNil(o.Static) {
		return true
	}

	return false
}

// SetStatic gets a reference to the given StaticCondition and assigns it to the Static field.
func (o *Condition) SetStatic(v StaticCondition) {
	o.Static = &v
}

// GetActions returns the Actions field value if set, zero value otherwise.
func (o *Condition) GetActions() []Action {
	if o == nil || IsNil(o.Actions) {
		var ret []Action
		return ret
	}
	return o.Actions
}

// GetActionsOk returns a tuple with the Actions field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Condition) GetActionsOk() ([]Action, bool) {
	if o == nil || IsNil(o.Actions) {
		return nil, false
	}
	return o.Actions, true
}

// HasActions returns a boolean if a field has been set.
func (o *Condition) HasActions() bool {
	if o != nil && !IsNil(o.Actions) {
		return true
	}

	return false
}

// SetActions gets a reference to the given []Action and assigns it to the Actions field.
func (o *Condition) SetActions(v []Action) {
	o.Actions = v
}

func (o Condition) MarshalJSON() ([]byte, error) {
	toSerialize, err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o Condition) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["name"] = o.Name
	if !IsNil(o.Type) {
		toSerialize["type"] = o.Type
	}
	if !IsNil(o.Static) {
		toSerialize["static"] = o.Static
	}
	if !IsNil(o.Actions) {
		toSerialize["actions"] = o.Actions
	}
	return toSerialize, nil
}

func (o *Condition) UnmarshalJSON(data []byte) (err error) {
	// This validates that all required properties are included in the JSON object
	// by unmarshalling the object into a generic map with string keys and checking
	// that every required field exists as a key in the generic map.
	requiredProperties := []string{
		"name",
	}

	allProperties := make(map[string]interface{})

	err = json.Unmarshal(data, &allProperties)

	if err != nil {
		return err
	}

	for _, requiredProperty := range requiredProperties {
		if _, exists := allProperties[requiredProperty]; !exists {
			return fmt.Errorf("no value given for required property %v", requiredProperty)
		}
	}

	varCondition := _Condition{}

	decoder := json.NewDecoder(bytes.NewReader(data))
	decoder.DisallowUnknownFields()
	err = decoder.Decode(&varCondition)

	if err != nil {
		return err
	}

	*o = Condition(varCondition)

	return err
}

type NullableCondition struct {
	value *Condition
	isSet bool
}

func (v NullableCondition) Get() *Condition {
	return v.value
}

func (v *NullableCondition) Set(val *Condition) {
	v.value = val
	v.isSet = true
}

func (v NullableCondition) IsSet() bool {
	return v.isSet
}

func (v *NullableCondition) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableCondition(val *Condition) *NullableCondition {
	return &NullableCondition{value: val, isSet: true}
}

func (v NullableCondition) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableCondition) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

```

--------------------------------------------------------------------------------
/images/Metoro_square.svg:
--------------------------------------------------------------------------------

```
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 595.28 595.28"><defs><style>.n{fill:none;}.o{fill:url(#k);}.p{fill:url(#j);}.q{fill:url(#i);}.r{fill:url(#h);}.s{fill:url(#l);}.t{fill:url(#g);}.u{fill:url(#f);}.v{fill:url(#e);}.w{fill:url(#d);}.x{fill:url(#c);}.y{fill:url(#b);}.z{fill:#50d086;}</style><linearGradient id="b" x1="345.76" y1="292.51" x2="362.34" y2="275.94" gradientUnits="userSpaceOnUse"><stop offset=".38" stop-color="#5b96f7"/><stop offset="1" stop-color="#37cb89"/></linearGradient><linearGradient id="c" x1="249.79" y1="253.86" x2="266.36" y2="237.29" xlink:href="#b"/><linearGradient id="d" x1="215.15" y1="354.44" x2="356.12" y2="213.47" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#5b96f7"/><stop offset=".42" stop-color="#34b1e6"/><stop offset="1" stop-color="#37cb89"/></linearGradient><linearGradient id="e" x1="193.98" y1="206.73" x2="193.98" y2="63.37" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#5b96f7"/><stop offset="1" stop-color="#34b1e6"/></linearGradient><linearGradient id="f" x1="285.32" y1="161.4" x2="285.32" y2="18.05" gradientUnits="userSpaceOnUse"><stop offset=".31" stop-color="#34b1e6"/><stop offset="1" stop-color="#37cb89"/></linearGradient><linearGradient id="g" x1="19.94" y1="488.8" x2="578.22" y2="488.8" xlink:href="#d"/><linearGradient id="h" x1="19.94" y1="488.8" x2="578.22" y2="488.8" xlink:href="#d"/><linearGradient id="i" x1="19.94" y1="488.8" x2="578.22" y2="488.8" xlink:href="#d"/><linearGradient id="j" x1="19.94" y1="488.8" x2="578.22" y2="488.8" xlink:href="#d"/><linearGradient id="k" x1="19.94" y1="488.8" x2="578.22" y2="488.8" xlink:href="#d"/><linearGradient id="l" x1="19.94" y1="488.8" x2="578.22" y2="488.8" xlink:href="#d"/></defs><g id="a"><path class="y" d="M365.77,284.22c0,6.47-5.25,11.72-11.72,11.72s-11.72-5.25-11.72-11.72,5.25-11.72,11.72-11.72,11.72,5.25,11.72,11.72Z"/><path class="x" d="M269.8,245.57c0,6.47-5.25,11.72-11.72,11.72s-11.72-5.25-11.72-11.72,5.25-11.72,11.72-11.72,11.72,5.25,11.72,11.72Z"/><path class="w" d="M285.64,184.27c-55.05,0-99.68,44.63-99.68,99.68,0,9.12,1.23,17.96,3.53,26.35,13.58.44,24.46,11.58,24.46,25.27,0,4.93-1.41,9.52-3.85,13.41,18.28,21.22,45.34,34.65,75.55,34.65,55.05,0,99.68-44.63,99.68-99.68s-44.63-99.68-99.68-99.68ZM230.83,245.59c0-15.06,12.21-27.26,27.26-27.26s27.26,12.21,27.26,27.26-12.21,27.26-27.26,27.26-27.26-12.21-27.26-27.26ZM277.6,334.46c-8.58,0-15.54-6.96-15.54-15.54s6.96-15.54,15.54-15.54,15.54,6.96,15.54,15.54-6.96,15.54-15.54,15.54ZM312.19,242.02c-6.47,0-11.72-5.25-11.72-11.72s5.25-11.72,11.72-11.72,11.72,5.25,11.72,11.72-5.25,11.72-11.72,11.72ZM354.08,308.22c-13.22,0-23.93-10.72-23.93-23.93s10.72-23.93,23.93-23.93,23.93,10.72,23.93,23.93-10.72,23.93-23.93,23.93Z"/><path class="z" d="M387.67,201.3c0,3-2.43,5.43-5.43,5.43s-5.43-2.43-5.43-5.43V68.8c0-3,2.43-5.43,5.43-5.43s5.43,2.43,5.43,5.43v132.5Z"/><path class="v" d="M199.41,201.3c0,3-2.43,5.43-5.43,5.43s-5.43-2.43-5.43-5.43V68.8c0-3,2.43-5.43,5.43-5.43s5.43,2.43,5.43,5.43v132.5Z"/><path class="u" d="M290.74,155.98c0,3-2.43,5.43-5.43,5.43s-5.43-2.43-5.43-5.43V23.48c0-3,2.43-5.43,5.43-5.43s5.43,2.43,5.43,5.43v132.5Z"/><path class="t" d="M19.94,428.29h31.1l32.85,80.14h1.4l32.85-80.14h31.1v119.27h-24.46v-77.63h-.99l-30.87,77.05h-16.66l-30.87-77.34h-.99v77.92h-24.46v-119.27Z"/><path class="r" d="M210.27,549.31c-9.2,0-17.11-1.87-23.73-5.62-6.62-3.75-11.72-9.07-15.29-15.96s-5.36-15.05-5.36-24.49,1.79-17.28,5.36-24.23c3.57-6.95,8.61-12.37,15.11-16.25,6.5-3.88,14.14-5.82,22.92-5.82,5.9,0,11.4.94,16.51,2.82,5.1,1.88,9.57,4.71,13.39,8.47,3.82,3.77,6.8,8.49,8.94,14.18,2.13,5.69,3.2,12.34,3.2,19.95v6.81h-75.54v-15.38h52.18c0-3.57-.78-6.74-2.33-9.49-1.55-2.76-3.7-4.92-6.44-6.49-2.74-1.57-5.91-2.36-9.52-2.36s-7.1.86-9.99,2.59c-2.89,1.73-5.15,4.04-6.79,6.93s-2.47,6.11-2.5,9.64v14.62c0,4.43.82,8.25,2.47,11.47s3.99,5.71,7.02,7.45,6.62,2.62,10.77,2.62c2.76,0,5.28-.39,7.57-1.17,2.29-.78,4.25-1.94,5.88-3.49s2.87-3.46,3.73-5.71l22.95,1.51c-1.17,5.51-3.54,10.32-7.13,14.41-3.59,4.1-8.21,7.28-13.86,9.55-5.65,2.27-12.16,3.41-19.54,3.41Z"/><path class="q" d="M315.09,458.11v18.64h-53.87v-18.64h53.87ZM273.45,436.68h24.81v83.4c0,2.29.35,4.07,1.05,5.33.7,1.26,1.68,2.15,2.94,2.65,1.26.5,2.73.76,4.4.76,1.17,0,2.33-.11,3.49-.32,1.17-.21,2.06-.38,2.68-.5l3.9,18.46c-1.24.39-2.99.84-5.24,1.37-2.25.52-4.99.84-8.21.96-5.98.23-11.21-.56-15.7-2.39-4.48-1.82-7.97-4.66-10.45-8.5-2.49-3.84-3.71-8.7-3.67-14.56v-86.66Z"/><path class="p" d="M370.71,549.31c-9.05,0-16.86-1.93-23.44-5.79-6.58-3.86-11.66-9.26-15.23-16.19-3.57-6.93-5.36-14.98-5.36-24.14s1.79-17.33,5.36-24.26c3.57-6.93,8.65-12.33,15.23-16.19s14.39-5.79,23.44-5.79,16.86,1.93,23.44,5.79c6.58,3.86,11.66,9.26,15.23,16.19,3.57,6.93,5.36,15.02,5.36,24.26s-1.79,17.21-5.36,24.14c-3.57,6.93-8.65,12.33-15.23,16.19-6.58,3.86-14.39,5.79-23.44,5.79ZM370.83,530.09c4.12,0,7.55-1.17,10.31-3.52,2.76-2.35,4.84-5.56,6.26-9.64,1.42-4.08,2.12-8.72,2.12-13.92s-.71-9.84-2.12-13.92c-1.42-4.08-3.5-7.3-6.26-9.67-2.76-2.37-6.19-3.55-10.31-3.55s-7.64,1.18-10.45,3.55c-2.82,2.37-4.93,5.59-6.35,9.67-1.42,4.08-2.13,8.72-2.13,13.92s.71,9.84,2.13,13.92c1.42,4.08,3.53,7.29,6.35,9.64,2.81,2.35,6.3,3.52,10.45,3.52Z"/><path class="o" d="M430.93,547.56v-89.45h24.05v15.61h.93c1.63-5.55,4.37-9.75,8.21-12.61s8.27-4.28,13.28-4.28c1.24,0,2.58.08,4.02.23,1.44.16,2.7.37,3.79.64v22.01c-1.16-.35-2.78-.66-4.83-.93-2.06-.27-3.94-.41-5.65-.41-3.65,0-6.9.79-9.75,2.36-2.85,1.57-5.11,3.76-6.76,6.55-1.65,2.8-2.48,6.02-2.48,9.67v50.61h-24.81Z"/><path class="s" d="M534.19,549.31c-9.05,0-16.86-1.93-23.44-5.79-6.58-3.86-11.66-9.26-15.23-16.19-3.57-6.93-5.36-14.98-5.36-24.14s1.79-17.33,5.36-24.26c3.57-6.93,8.65-12.33,15.23-16.19s14.39-5.79,23.44-5.79,16.86,1.93,23.44,5.79c6.58,3.86,11.66,9.26,15.23,16.19,3.57,6.93,5.36,15.02,5.36,24.26s-1.79,17.21-5.36,24.14c-3.57,6.93-8.65,12.33-15.23,16.19-6.58,3.86-14.39,5.79-23.44,5.79ZM534.3,530.09c4.12,0,7.55-1.17,10.31-3.52,2.76-2.35,4.84-5.56,6.26-9.64,1.42-4.08,2.12-8.72,2.12-13.92s-.71-9.84-2.12-13.92c-1.42-4.08-3.5-7.3-6.26-9.67-2.76-2.37-6.19-3.55-10.31-3.55s-7.64,1.18-10.45,3.55c-2.82,2.37-4.93,5.59-6.35,9.67-1.42,4.08-2.13,8.72-2.13,13.92s.71,9.84,2.13,13.92c1.42,4.08,3.53,7.29,6.35,9.64,2.81,2.35,6.3,3.52,10.45,3.52Z"/></g><g id="m"><rect class="n" width="595.28" height="595.28"/></g></svg>
```

--------------------------------------------------------------------------------
/model/model_action_webhook_destination.go:
--------------------------------------------------------------------------------

```go
/*
Metoro API

API for managing Metoro environments, alerts, and dashboards.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"encoding/json"
)

// checks if the ActionWebhookDestination type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &ActionWebhookDestination{}

// ActionWebhookDestination struct for ActionWebhookDestination
type ActionWebhookDestination struct {
	// UUID of the webhook configuration to use for notifying that the alert is triggered
	Uuid *string `json:"uuid,omitempty"`
	// Name of the webhook configuration to use for notifying that the alert is triggered
	Name *string `json:"name,omitempty"`
	// UUID of the webhook configuration to use for notifying that the alert is resolved
	ResolvedUuid *string `json:"resolved_uuid,omitempty"`
	// Name of the webhook configuration to use for notifying that the alert is resolved
	ResolvedName *string `json:"resolved_name,omitempty"`
}

// NewActionWebhookDestination instantiates a new ActionWebhookDestination object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewActionWebhookDestination() *ActionWebhookDestination {
	this := ActionWebhookDestination{}
	return &this
}

// NewActionWebhookDestinationWithDefaults instantiates a new ActionWebhookDestination object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewActionWebhookDestinationWithDefaults() *ActionWebhookDestination {
	this := ActionWebhookDestination{}
	return &this
}

// GetUuid returns the Uuid field value if set, zero value otherwise.
func (o *ActionWebhookDestination) GetUuid() string {
	if o == nil || IsNil(o.Uuid) {
		var ret string
		return ret
	}
	return *o.Uuid
}

// GetUuidOk returns a tuple with the Uuid field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ActionWebhookDestination) GetUuidOk() (*string, bool) {
	if o == nil || IsNil(o.Uuid) {
		return nil, false
	}
	return o.Uuid, true
}

// HasUuid returns a boolean if a field has been set.
func (o *ActionWebhookDestination) HasUuid() bool {
	if o != nil && !IsNil(o.Uuid) {
		return true
	}

	return false
}

// SetUuid gets a reference to the given string and assigns it to the Uuid field.
func (o *ActionWebhookDestination) SetUuid(v string) {
	o.Uuid = &v
}

// GetName returns the Name field value if set, zero value otherwise.
func (o *ActionWebhookDestination) GetName() string {
	if o == nil || IsNil(o.Name) {
		var ret string
		return ret
	}
	return *o.Name
}

// GetNameOk returns a tuple with the Name field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ActionWebhookDestination) GetNameOk() (*string, bool) {
	if o == nil || IsNil(o.Name) {
		return nil, false
	}
	return o.Name, true
}

// HasName returns a boolean if a field has been set.
func (o *ActionWebhookDestination) HasName() bool {
	if o != nil && !IsNil(o.Name) {
		return true
	}

	return false
}

// SetName gets a reference to the given string and assigns it to the Name field.
func (o *ActionWebhookDestination) SetName(v string) {
	o.Name = &v
}

// GetResolvedUuid returns the ResolvedUuid field value if set, zero value otherwise.
func (o *ActionWebhookDestination) GetResolvedUuid() string {
	if o == nil || IsNil(o.ResolvedUuid) {
		var ret string
		return ret
	}
	return *o.ResolvedUuid
}

// GetResolvedUuidOk returns a tuple with the ResolvedUuid field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ActionWebhookDestination) GetResolvedUuidOk() (*string, bool) {
	if o == nil || IsNil(o.ResolvedUuid) {
		return nil, false
	}
	return o.ResolvedUuid, true
}

// HasResolvedUuid returns a boolean if a field has been set.
func (o *ActionWebhookDestination) HasResolvedUuid() bool {
	if o != nil && !IsNil(o.ResolvedUuid) {
		return true
	}

	return false
}

// SetResolvedUuid gets a reference to the given string and assigns it to the ResolvedUuid field.
func (o *ActionWebhookDestination) SetResolvedUuid(v string) {
	o.ResolvedUuid = &v
}

// GetResolvedName returns the ResolvedName field value if set, zero value otherwise.
func (o *ActionWebhookDestination) GetResolvedName() string {
	if o == nil || IsNil(o.ResolvedName) {
		var ret string
		return ret
	}
	return *o.ResolvedName
}

// GetResolvedNameOk returns a tuple with the ResolvedName field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ActionWebhookDestination) GetResolvedNameOk() (*string, bool) {
	if o == nil || IsNil(o.ResolvedName) {
		return nil, false
	}
	return o.ResolvedName, true
}

// HasResolvedName returns a boolean if a field has been set.
func (o *ActionWebhookDestination) HasResolvedName() bool {
	if o != nil && !IsNil(o.ResolvedName) {
		return true
	}

	return false
}

// SetResolvedName gets a reference to the given string and assigns it to the ResolvedName field.
func (o *ActionWebhookDestination) SetResolvedName(v string) {
	o.ResolvedName = &v
}

func (o ActionWebhookDestination) MarshalJSON() ([]byte, error) {
	toSerialize,err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o ActionWebhookDestination) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.Uuid) {
		toSerialize["uuid"] = o.Uuid
	}
	if !IsNil(o.Name) {
		toSerialize["name"] = o.Name
	}
	if !IsNil(o.ResolvedUuid) {
		toSerialize["resolved_uuid"] = o.ResolvedUuid
	}
	if !IsNil(o.ResolvedName) {
		toSerialize["resolved_name"] = o.ResolvedName
	}
	return toSerialize, nil
}

type NullableActionWebhookDestination struct {
	value *ActionWebhookDestination
	isSet bool
}

func (v NullableActionWebhookDestination) Get() *ActionWebhookDestination {
	return v.value
}

func (v *NullableActionWebhookDestination) Set(val *ActionWebhookDestination) {
	v.value = val
	v.isSet = true
}

func (v NullableActionWebhookDestination) IsSet() bool {
	return v.isSet
}

func (v *NullableActionWebhookDestination) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableActionWebhookDestination(val *ActionWebhookDestination) *NullableActionWebhookDestination {
	return &NullableActionWebhookDestination{value: val, isSet: true}
}

func (v NullableActionWebhookDestination) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableActionWebhookDestination) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}



```

--------------------------------------------------------------------------------
/model/utils.go:
--------------------------------------------------------------------------------

```go
/*
Metoro API

API for managing Metoro environments, alerts, and dashboards.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"bytes"
	"encoding/json"
	"fmt"
	"reflect"
	"time"
)

// PtrBool is a helper routine that returns a pointer to given boolean value.
func PtrBool(v bool) *bool { return &v }

// PtrInt is a helper routine that returns a pointer to given integer value.
func PtrInt(v int) *int { return &v }

// PtrInt32 is a helper routine that returns a pointer to given integer value.
func PtrInt32(v int32) *int32 { return &v }

// PtrInt64 is a helper routine that returns a pointer to given integer value.
func PtrInt64(v int64) *int64 { return &v }

// PtrFloat32 is a helper routine that returns a pointer to given float value.
func PtrFloat32(v float32) *float32 { return &v }

// PtrFloat64 is a helper routine that returns a pointer to given float value.
func PtrFloat64(v float64) *float64 { return &v }

// PtrString is a helper routine that returns a pointer to given string value.
func PtrString(v string) *string { return &v }

// PtrTime is helper routine that returns a pointer to given Time value.
func PtrTime(v time.Time) *time.Time { return &v }

type NullableBool struct {
	value *bool
	isSet bool
}

func (v NullableBool) Get() *bool {
	return v.value
}

func (v *NullableBool) Set(val *bool) {
	v.value = val
	v.isSet = true
}

func (v NullableBool) IsSet() bool {
	return v.isSet
}

func (v *NullableBool) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableBool(val *bool) *NullableBool {
	return &NullableBool{value: val, isSet: true}
}

func (v NullableBool) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableBool) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

type NullableInt struct {
	value *int
	isSet bool
}

func (v NullableInt) Get() *int {
	return v.value
}

func (v *NullableInt) Set(val *int) {
	v.value = val
	v.isSet = true
}

func (v NullableInt) IsSet() bool {
	return v.isSet
}

func (v *NullableInt) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableInt(val *int) *NullableInt {
	return &NullableInt{value: val, isSet: true}
}

func (v NullableInt) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableInt) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

type NullableInt32 struct {
	value *int32
	isSet bool
}

func (v NullableInt32) Get() *int32 {
	return v.value
}

func (v *NullableInt32) Set(val *int32) {
	v.value = val
	v.isSet = true
}

func (v NullableInt32) IsSet() bool {
	return v.isSet
}

func (v *NullableInt32) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableInt32(val *int32) *NullableInt32 {
	return &NullableInt32{value: val, isSet: true}
}

func (v NullableInt32) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableInt32) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

type NullableInt64 struct {
	value *int64
	isSet bool
}

func (v NullableInt64) Get() *int64 {
	return v.value
}

func (v *NullableInt64) Set(val *int64) {
	v.value = val
	v.isSet = true
}

func (v NullableInt64) IsSet() bool {
	return v.isSet
}

func (v *NullableInt64) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableInt64(val *int64) *NullableInt64 {
	return &NullableInt64{value: val, isSet: true}
}

func (v NullableInt64) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableInt64) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

type NullableFloat32 struct {
	value *float32
	isSet bool
}

func (v NullableFloat32) Get() *float32 {
	return v.value
}

func (v *NullableFloat32) Set(val *float32) {
	v.value = val
	v.isSet = true
}

func (v NullableFloat32) IsSet() bool {
	return v.isSet
}

func (v *NullableFloat32) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableFloat32(val *float32) *NullableFloat32 {
	return &NullableFloat32{value: val, isSet: true}
}

func (v NullableFloat32) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableFloat32) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

type NullableFloat64 struct {
	value *float64
	isSet bool
}

func (v NullableFloat64) Get() *float64 {
	return v.value
}

func (v *NullableFloat64) Set(val *float64) {
	v.value = val
	v.isSet = true
}

func (v NullableFloat64) IsSet() bool {
	return v.isSet
}

func (v *NullableFloat64) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableFloat64(val *float64) *NullableFloat64 {
	return &NullableFloat64{value: val, isSet: true}
}

func (v NullableFloat64) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableFloat64) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

type NullableString struct {
	value *string
	isSet bool
}

func (v NullableString) Get() *string {
	return v.value
}

func (v *NullableString) Set(val *string) {
	v.value = val
	v.isSet = true
}

func (v NullableString) IsSet() bool {
	return v.isSet
}

func (v *NullableString) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableString(val *string) *NullableString {
	return &NullableString{value: val, isSet: true}
}

func (v NullableString) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableString) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

type NullableTime struct {
	value *time.Time
	isSet bool
}

func (v NullableTime) Get() *time.Time {
	return v.value
}

func (v *NullableTime) Set(val *time.Time) {
	v.value = val
	v.isSet = true
}

func (v NullableTime) IsSet() bool {
	return v.isSet
}

func (v *NullableTime) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableTime(val *time.Time) *NullableTime {
	return &NullableTime{value: val, isSet: true}
}

func (v NullableTime) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableTime) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

// IsNil checks if an input is nil
func IsNil(i interface{}) bool {
	if i == nil {
		return true
	}
	switch reflect.TypeOf(i).Kind() {
	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
		return reflect.ValueOf(i).IsNil()
	case reflect.Array:
		return reflect.ValueOf(i).IsZero()
	}
	return false
}

type MappedNullable interface {
	ToMap() (map[string]interface{}, error)
}

// A wrapper for strict JSON decoding
func newStrictDecoder(data []byte) *json.Decoder {
	dec := json.NewDecoder(bytes.NewBuffer(data))
	dec.DisallowUnknownFields()
	return dec
}

// Prevent trying to import "fmt"
func reportError(format string, a ...interface{}) error {
	return fmt.Errorf(format, a...)
}

```

--------------------------------------------------------------------------------
/utils/time_utils_test.go:
--------------------------------------------------------------------------------

```go
package utils

import (
	"strings"
	"testing"
	"time"
)

func TestCalculateTimeRange(t *testing.T) {
	// Set IS_PROD for tests that need to validate 30-day window
	t.Setenv("IS_PROD", "true")
	
	// Helper function to create pointers
	strPtr := func(s string) *string { return &s }
	intPtr := func(i int) *int { return &i }
	timeWindowPtr := func(tw TimeWindow) *TimeWindow { return &tw }

	tests := []struct {
		name          string
		config        TimeConfig
		wantPeriod    *time.Duration // Expected time difference between start and end for relative time
		wantStartTime *time.Time     // Expected exact start time for absolute time
		wantEndTime   *time.Time     // Expected exact end time for absolute time
		wantErr       bool
		errMsg        string
	}{
		// Relative time tests
		{
			name: "relative - 5 minutes",
			config: TimeConfig{
				Type:       RelativeTimeRange,
				TimePeriod: intPtr(5),
				TimeWindow: timeWindowPtr(Minutes),
			},
			wantPeriod: func() *time.Duration {
				d := 5 * time.Minute
				return &d
			}(),
		},
		{
			name: "relative - 2 hours",
			config: TimeConfig{
				Type:       RelativeTimeRange,
				TimePeriod: intPtr(2),
				TimeWindow: timeWindowPtr(Hours),
			},
			wantPeriod: func() *time.Duration {
				d := 2 * time.Hour
				return &d
			}(),
		},
		{
			name: "relative - missing time period",
			config: TimeConfig{
				Type:       RelativeTimeRange,
				TimeWindow: timeWindowPtr(Minutes),
			},
			wantErr: true,
			errMsg:  "time_period and time_window are required for relative time range",
		},
		{
			name: "relative - invalid time window",
			config: TimeConfig{
				Type:       RelativeTimeRange,
				TimePeriod: intPtr(5),
				TimeWindow: timeWindowPtr("invalid"),
			},
			wantErr: true,
			errMsg:  "invalid time window: invalid",
		},
		{
			name: "relative - exceeds 30 days",
			config: TimeConfig{
				Type:       RelativeTimeRange,
				TimePeriod: intPtr(31),
				TimeWindow: timeWindowPtr(Days),
			},
			wantErr: true,
			errMsg:  "time range cannot exceed 30 days",
		},
		{
			name: "relative - exactly 30 days",
			config: TimeConfig{
				Type:       RelativeTimeRange,
				TimePeriod: intPtr(30),
				TimeWindow: timeWindowPtr(Days),
			},
			wantPeriod: func() *time.Duration {
				d := 30 * 24 * time.Hour
				return &d
			}(),
		},

		// Absolute time tests
		{
			name: "absolute - valid time range",
			config: TimeConfig{
				Type:      AbsoluteTimeRange,
				StartTime: strPtr(time.Now().Add(-24 * time.Hour).Format(time.RFC3339)),
				EndTime:   strPtr(time.Now().Add(-23 * time.Hour).Format(time.RFC3339)),
			},
			wantStartTime: func() *time.Time {
				t := time.Now().Add(-24 * time.Hour)
				return &t
			}(),
			wantEndTime: func() *time.Time {
				t := time.Now().Add(-23 * time.Hour)
				return &t
			}(),
		},
		{
			name: "absolute - missing start time",
			config: TimeConfig{
				Type:    AbsoluteTimeRange,
				EndTime: strPtr("2024-12-12T15:00:00Z"),
			},
			wantErr: true,
			errMsg:  "start_time and end_time are required for absolute time range",
		},
		{
			name: "absolute - invalid start time format",
			config: TimeConfig{
				Type:      AbsoluteTimeRange,
				StartTime: strPtr("invalid"),
				EndTime:   strPtr("2024-12-12T15:00:00Z"),
			},
			wantErr: true,
			errMsg:  "invalid start_time format",
		},
		{
			name: "absolute - end time before start time",
			config: TimeConfig{
				Type:      AbsoluteTimeRange,
				StartTime: strPtr("2024-12-12T15:00:00Z"),
				EndTime:   strPtr("2024-12-12T14:00:00Z"),
			},
			wantErr: true,
			errMsg:  "end_time cannot be before start_time",
		},
		{
			name: "absolute - exceeds 30 days",
			config: TimeConfig{
				Type:      AbsoluteTimeRange,
				StartTime: strPtr(time.Now().Add(-31 * 24 * time.Hour).Format(time.RFC3339)),
				EndTime:   strPtr(time.Now().Format(time.RFC3339)),
			},
			wantErr: true,
			errMsg:  "time range cannot exceed 30 days",
		},
		{
			name: "absolute - exactly 30 days ago",
			config: TimeConfig{
				Type:      AbsoluteTimeRange,
				StartTime: strPtr(time.Now().Add(-30*24*time.Hour + 1*time.Hour).Format(time.RFC3339)),
				EndTime:   strPtr(time.Now().Add(-29 * 24 * time.Hour).Format(time.RFC3339)),
			},
			wantStartTime: func() *time.Time {
				t := time.Now().Add(-30*24*time.Hour + 1*time.Hour)
				return &t
			}(),
			wantEndTime: func() *time.Time {
				t := time.Now().Add(-29 * 24 * time.Hour)
				return &t
			}(),
		},

		// Invalid type test
		{
			name: "invalid time range type",
			config: TimeConfig{
				Type: "invalid",
			},
			wantErr: true,
			errMsg:  "invalid time range type: invalid",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			startTime, endTime, err := CalculateTimeRange(tt.config)

			// Check error cases
			if tt.wantErr {
				if err == nil {
					t.Errorf("CalculateTimeRange() error = nil, wantErr %v", tt.wantErr)
					return
				}
				if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) {
					t.Errorf("CalculateTimeRange() error = %v, want error containing %v", err, tt.errMsg)
				}
				return
			}

			if err != nil {
				t.Errorf("CalculateTimeRange() unexpected error = %v", err)
				return
			}

			// For relative time tests
			if tt.wantPeriod != nil {
				startTimeObj := time.Unix(startTime, 0)
				endTimeObj := time.Unix(endTime, 0)
				gotPeriod := endTimeObj.Sub(startTimeObj)
				if gotPeriod != *tt.wantPeriod {
					t.Errorf("CalculateTimeRange() time period = %v, want %v", gotPeriod, *tt.wantPeriod)
				}

				// Check if endTime is approximately now (within 1 second tolerance)
				nowUnix := time.Now().Unix()
				if diff := abs(endTime - nowUnix); diff > 1 {
					t.Errorf("CalculateTimeRange() endTime is not close enough to current time. diff = %v seconds", diff)
				}
			}

			// For absolute time tests
			if tt.wantStartTime != nil && tt.wantEndTime != nil {
				// For dynamic times (using time.Now()), check time range validity and approximate values
				if strings.Contains(tt.name, "30 days ago") || strings.Contains(tt.name, "valid time range") {
					// Just check the time range is valid
					if endTime < startTime {
						t.Errorf("CalculateTimeRange() endTime < startTime")
					}
					// Check approximate values (within 2 seconds tolerance)
					if diff := abs(startTime - tt.wantStartTime.Unix()); diff > 2 {
						t.Errorf("CalculateTimeRange() startTime difference too large: %v seconds", diff)
					}
					if diff := abs(endTime - tt.wantEndTime.Unix()); diff > 2 {
						t.Errorf("CalculateTimeRange() endTime difference too large: %v seconds", diff)
					}
				} else {
					// For static times, check exact match
					if startTime != tt.wantStartTime.Unix() {
						t.Errorf("CalculateTimeRange() startTime = %v, want %v", startTime, tt.wantStartTime.Unix())
					}
					if endTime != tt.wantEndTime.Unix() {
						t.Errorf("CalculateTimeRange() endTime = %v, want %v", endTime, tt.wantEndTime.Unix())
					}
				}
			}
		})
	}
}

func abs(x int64) int64 {
	if x < 0 {
		return -x
	}
	return x
}

```

--------------------------------------------------------------------------------
/model/model_persistence_settings.go:
--------------------------------------------------------------------------------

```go
/*
Metoro API

API for managing Metoro environments, alerts, and dashboards.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"bytes"
	"encoding/json"
	"fmt"
)

// checks if the PersistenceSettings type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &PersistenceSettings{}

// PersistenceSettings struct for PersistenceSettings
type PersistenceSettings struct {
	// Number of data points that must breach the threshold within the evaluation window to trigger an alert.
	DatapointsToAlarm int64 `json:"datapointsToAlarm"`
	// Total number of data points in the evaluation window. If your bucketSize is 60 (seconds) and datapointsInEvaluationWindow is 5, then the evaluation window is 5 minutes.
	DatapointsInEvaluationWindow int64 `json:"datapointsInEvaluationWindow"`
	// Determines how missing data points are treated - either as breaching the threshold or not breaching.
	MissingDatapointBehavior *string `json:"missingDatapointBehavior,omitempty"`
}

type _PersistenceSettings PersistenceSettings

// NewPersistenceSettings instantiates a new PersistenceSettings object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewPersistenceSettings(datapointsToAlarm int64, datapointsInEvaluationWindow int64) *PersistenceSettings {
	this := PersistenceSettings{}
	this.DatapointsToAlarm = datapointsToAlarm
	this.DatapointsInEvaluationWindow = datapointsInEvaluationWindow
	var missingDatapointBehavior string = "notBreaching"
	this.MissingDatapointBehavior = &missingDatapointBehavior
	return &this
}

// NewPersistenceSettingsWithDefaults instantiates a new PersistenceSettings object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewPersistenceSettingsWithDefaults() *PersistenceSettings {
	this := PersistenceSettings{}
	var missingDatapointBehavior string = "notBreaching"
	this.MissingDatapointBehavior = &missingDatapointBehavior
	return &this
}

// GetDatapointsToAlarm returns the DatapointsToAlarm field value
func (o *PersistenceSettings) GetDatapointsToAlarm() int64 {
	if o == nil {
		var ret int64
		return ret
	}

	return o.DatapointsToAlarm
}

// GetDatapointsToAlarmOk returns a tuple with the DatapointsToAlarm field value
// and a boolean to check if the value has been set.
func (o *PersistenceSettings) GetDatapointsToAlarmOk() (*int64, bool) {
	if o == nil {
		return nil, false
	}
	return &o.DatapointsToAlarm, true
}

// SetDatapointsToAlarm sets field value
func (o *PersistenceSettings) SetDatapointsToAlarm(v int64) {
	o.DatapointsToAlarm = v
}

// GetDatapointsInEvaluationWindow returns the DatapointsInEvaluationWindow field value
func (o *PersistenceSettings) GetDatapointsInEvaluationWindow() int64 {
	if o == nil {
		var ret int64
		return ret
	}

	return o.DatapointsInEvaluationWindow
}

// GetDatapointsInEvaluationWindowOk returns a tuple with the DatapointsInEvaluationWindow field value
// and a boolean to check if the value has been set.
func (o *PersistenceSettings) GetDatapointsInEvaluationWindowOk() (*int64, bool) {
	if o == nil {
		return nil, false
	}
	return &o.DatapointsInEvaluationWindow, true
}

// SetDatapointsInEvaluationWindow sets field value
func (o *PersistenceSettings) SetDatapointsInEvaluationWindow(v int64) {
	o.DatapointsInEvaluationWindow = v
}

// GetMissingDatapointBehavior returns the MissingDatapointBehavior field value if set, zero value otherwise.
func (o *PersistenceSettings) GetMissingDatapointBehavior() string {
	if o == nil || IsNil(o.MissingDatapointBehavior) {
		var ret string
		return ret
	}
	return *o.MissingDatapointBehavior
}

// GetMissingDatapointBehaviorOk returns a tuple with the MissingDatapointBehavior field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *PersistenceSettings) GetMissingDatapointBehaviorOk() (*string, bool) {
	if o == nil || IsNil(o.MissingDatapointBehavior) {
		return nil, false
	}
	return o.MissingDatapointBehavior, true
}

// HasMissingDatapointBehavior returns a boolean if a field has been set.
func (o *PersistenceSettings) HasMissingDatapointBehavior() bool {
	if o != nil && !IsNil(o.MissingDatapointBehavior) {
		return true
	}

	return false
}

// SetMissingDatapointBehavior gets a reference to the given string and assigns it to the MissingDatapointBehavior field.
func (o *PersistenceSettings) SetMissingDatapointBehavior(v string) {
	o.MissingDatapointBehavior = &v
}

func (o PersistenceSettings) MarshalJSON() ([]byte, error) {
	toSerialize, err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o PersistenceSettings) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["datapointsToAlarm"] = o.DatapointsToAlarm
	toSerialize["datapointsInEvaluationWindow"] = o.DatapointsInEvaluationWindow
	if !IsNil(o.MissingDatapointBehavior) {
		toSerialize["missingDatapointBehavior"] = o.MissingDatapointBehavior
	}
	return toSerialize, nil
}

func (o *PersistenceSettings) UnmarshalJSON(data []byte) (err error) {
	// This validates that all required properties are included in the JSON object
	// by unmarshalling the object into a generic map with string keys and checking
	// that every required field exists as a key in the generic map.
	requiredProperties := []string{
		"datapointsToAlarm",
		"datapointsInEvaluationWindow",
	}

	allProperties := make(map[string]interface{})

	err = json.Unmarshal(data, &allProperties)

	if err != nil {
		return err
	}

	for _, requiredProperty := range requiredProperties {
		if _, exists := allProperties[requiredProperty]; !exists {
			return fmt.Errorf("no value given for required property %v", requiredProperty)
		}
	}

	varPersistenceSettings := _PersistenceSettings{}

	decoder := json.NewDecoder(bytes.NewReader(data))
	decoder.DisallowUnknownFields()
	err = decoder.Decode(&varPersistenceSettings)

	if err != nil {
		return err
	}

	*o = PersistenceSettings(varPersistenceSettings)

	return err
}

type NullablePersistenceSettings struct {
	value *PersistenceSettings
	isSet bool
}

func (v NullablePersistenceSettings) Get() *PersistenceSettings {
	return v.value
}

func (v *NullablePersistenceSettings) Set(val *PersistenceSettings) {
	v.value = val
	v.isSet = true
}

func (v NullablePersistenceSettings) IsSet() bool {
	return v.isSet
}

func (v *NullablePersistenceSettings) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullablePersistenceSettings(val *PersistenceSettings) *NullablePersistenceSettings {
	return &NullablePersistenceSettings{value: val, isSet: true}
}

func (v NullablePersistenceSettings) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullablePersistenceSettings) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

```

--------------------------------------------------------------------------------
/tools/get_multi_metric.go:
--------------------------------------------------------------------------------

```go
package tools

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"slices"
	"strings"

	"github.com/metoro-io/metoro-mcp-server/model"
	"github.com/metoro-io/metoro-mcp-server/utils"

	mcpgolang "github.com/metoro-io/mcp-golang"
)

type GetMultiMetricHandlerArgs struct {
	TimeConfig utils.TimeConfig                `json:"time_config" jsonschema:"required,description=The time period to get the timeseries data for. e.g. if you want to get the timeseries data for the last 5 minutes you would set time_period=5 and time_window=Minutes. You can also set an absoulute time range by setting start_time and end_time"`
	Timeseries []model.SingleTimeseriesRequest `json:"timeseries" jsonschema:"required,description=Array of timeseries data to get. Each item in this array corresponds to a single timeseries. You can then use the formulas to combine these timeseries. If you only want to see the combination of timeseries via defining formulas and if you dont want to see the individual timeseries data when setting formulas you can set shouldNotReturn to true"`
	Formulas   []model.Formula                 `json:"formulas" jsonschema:"description=Optional formulas to combine timeseries. Formula should only consist of formulaIdentifier of the timeseries in the timeseries array. e.g. a + b + c if a b c appears in the formulaIdentifier of the timeseries array. You can ONLY do the following operations: Arithmetic operations:+ (for add) - (for substract) * (for multiply) / (for division) % (for modulus) ^ or ** (for exponent). Comparison: == != < > <= >= . Logical:! (for not) && (for AND) || (for OR). Conditional operations: ?: (ternary) e.g. (a || b) ? 1 : 0. Do not guess the operations. Just use these available ones!"`
}

func GetMultiMetricHandler(ctx context.Context, arguments GetMultiMetricHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}

	err = checkTimeseries(ctx, arguments.Timeseries, startTime, endTime)
	if err != nil {
		return nil, err
	}

	request := model.GetMultiMetricRequest{
		StartTime: startTime,
		EndTime:   endTime,
		Metrics:   convertTimeseriesToAPITimeseries(arguments.Timeseries, startTime, endTime),
		Formulas:  arguments.Formulas,
	}

	if len(arguments.Timeseries) == 0 {
		return nil, fmt.Errorf("no timeseries data provided")
	}

	body, err := getMultiMetricMetoroCall(ctx, request)
	if err != nil {
		return nil, fmt.Errorf("error getting metric: %v", err)
	}
	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

func getMultiMetricMetoroCall(ctx context.Context, request model.GetMultiMetricRequest) ([]byte, error) {
	requestBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling metric request: %v", err)
	}
	return utils.MakeMetoroAPIRequest("POST", "metrics", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
}

func convertTimeseriesToAPITimeseries(timeseries []model.SingleTimeseriesRequest, startTime int64, endTime int64) []model.SingleMetricRequest {
	result := make([]model.SingleMetricRequest, len(timeseries))

	for i, ts := range timeseries {
		apiRequest := model.SingleMetricRequest{
			Type:              string(ts.Type),
			ShouldNotReturn:   ts.ShouldNotReturn,
			FormulaIdentifier: ts.FormulaIdentifier,
		}

		switch ts.Type {
		case model.Metric:
			apiRequest.Metric = &model.GetMetricRequest{
				StartTime:      startTime,
				EndTime:        endTime,
				MetricName:     ts.MetricName,
				Filters:        ts.Filters,
				ExcludeFilters: ts.ExcludeFilters,
				Splits:         ts.Splits,
				Aggregation:    ts.Aggregation,
				Functions:      ts.Functions,
				//Functions:      ts.Metric.Functions,
				//LimitResults:   ts.Metric.LimitResults,
				BucketSize: ts.BucketSize,
			}

		case model.Trace:
			apiRequest.Trace = &model.GetTraceMetricRequest{
				StartTime:      startTime,
				EndTime:        endTime,
				Filters:        ts.Filters,
				ExcludeFilters: ts.ExcludeFilters,
				Splits:         ts.Splits,
				Aggregate:      ts.Aggregation,
				BucketSize:     ts.BucketSize,
				Functions:      ts.Functions,
				//ServiceNames:   ts.ServiceNames,
				//Regexes:        ts.Regexes,
				//ExcludeRegexes: ts.ExcludeRegexes,
				//Environments:   ts.Environments,
				//LimitResults:   ts.LimitResults,
				//
			}

		case model.Logs:
			apiRequest.Logs = &model.GetLogMetricRequest{
				GetLogsRequest: model.GetLogsRequest{
					StartTime:      startTime,
					EndTime:        endTime,
					Filters:        ts.Filters,
					ExcludeFilters: ts.ExcludeFilters,
					Regexes:        ts.Regexes,
					ExcludeRegexes: ts.ExcludeRegexes,
					//Environments:   ts.Environments,
				},
				Functions:  ts.Functions,
				Splits:     ts.Splits,
				BucketSize: ts.BucketSize,
				//Functions:  ts.Functions,
			}
		case model.KubernetesResource:
			apiRequest.KubernetesResource = &model.GetKubernetesResourceRequest{
				StartTime:      startTime,
				EndTime:        endTime,
				Filters:        ts.Filters,
				ExcludeFilters: ts.ExcludeFilters,
				Splits:         ts.Splits,
				BucketSize:     ts.BucketSize,
				Functions:      ts.Functions,
				JsonPath:       ts.JsonPath,
				Aggregation:    ts.Aggregation,
			}
		}
		result[i] = apiRequest
	}

	return result
}

func CheckAttributes(ctx context.Context, requestType model.MetricType, filters map[string][]string, excludeFilters map[string][]string, splits []string, metricRequest *model.GetMetricAttributesRequest) error {
	// Check whether the attributes given are valid.
	request := model.MultiMetricAttributeKeysRequest{
		Type:   string(requestType),
		Metric: metricRequest,
	}
	jsonBody, err := json.Marshal(request)
	if err != nil {
		return fmt.Errorf("error marshaling request: %v", err)
	}

	attributeResp, err := utils.MakeMetoroAPIRequest("POST", "metrics/attributes", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return fmt.Errorf("error making Metoro call: %v", err)
	}

	attributeKeys := model.GetAttributeKeysResponse{}
	err = json.Unmarshal(attributeResp, &attributeKeys)
	if err != nil {
		return fmt.Errorf("error unmarshaling response: %v", err)
	}

	attributesAsString := strings.Join(attributeKeys.Attributes, ", ")

	// Check whether the filters given are valid.
	for key, _ := range filters {
		if !slices.Contains(attributeKeys.Attributes, key) {
			return fmt.Errorf("invalid filter key: %s. Valid filter keys are: %s. Please try again with a valid key", key, attributesAsString)
		}
	}

	for key, _ := range excludeFilters {
		if !slices.Contains(attributeKeys.Attributes, key) {
			return fmt.Errorf("invalid exclude filter key: %s. Valid keys are: %s. Please try again with a valid key", key, attributesAsString)
		}
	}

	for _, split := range splits {
		if !slices.Contains(attributeKeys.Attributes, split) {
			return fmt.Errorf("invalid split key: %s. Valid keys are: %s. Please try again with a valid key", split, attributesAsString)
		}
	}
	return nil
}

func checkTimeseries(ctx context.Context, timeseries []model.SingleTimeseriesRequest, startTime, endTime int64) error {
	for _, ts := range timeseries {
		switch ts.Type {
		case model.Metric:
			err := CheckMetric(ctx, ts.MetricName)
			if err != nil {
				return err
			}
			err = CheckAttributes(ctx, ts.Type, ts.Filters, ts.ExcludeFilters, ts.Splits, &model.GetMetricAttributesRequest{
				StartTime:  startTime,
				EndTime:    endTime,
				MetricName: ts.MetricName,
			})
			if err != nil {
				return err
			}
		case model.Trace:
			err := CheckAttributes(ctx, ts.Type, ts.Filters, ts.ExcludeFilters, ts.Splits, nil)
			if err != nil {
				return err
			}
		case model.Logs:
			err := CheckAttributes(ctx, ts.Type, ts.Filters, ts.ExcludeFilters, ts.Splits, nil)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

```

--------------------------------------------------------------------------------
/model/model_action.go:
--------------------------------------------------------------------------------

```go
/*
Metoro API

API for managing Metoro environments, alerts, and dashboards.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"bytes"
	"encoding/json"
	"fmt"
)

// checks if the Action type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &Action{}

// Action struct for Action
type Action struct {
	// Type of action destination
	Type                 string                      `json:"type"`
	SlackDestination     *ActionSlackDestination     `json:"slackDestination,omitempty"`
	PagerDutyDestination *ActionPagerDutyDestination `json:"pagerDutyDestination,omitempty"`
	EmailDestination     *ActionEmailDestination     `json:"emailDestination,omitempty"`
	WebhookDestination   *ActionWebhookDestination   `json:"webhookDestination,omitempty"`
}

type _Action Action

// NewAction instantiates a new Action object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewAction(type_ string) *Action {
	this := Action{}
	this.Type = type_
	return &this
}

// NewActionWithDefaults instantiates a new Action object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewActionWithDefaults() *Action {
	this := Action{}
	return &this
}

// GetType returns the Type field value
func (o *Action) GetType() string {
	if o == nil {
		var ret string
		return ret
	}

	return o.Type
}

// GetTypeOk returns a tuple with the Type field value
// and a boolean to check if the value has been set.
func (o *Action) GetTypeOk() (*string, bool) {
	if o == nil {
		return nil, false
	}
	return &o.Type, true
}

// SetType sets field value
func (o *Action) SetType(v string) {
	o.Type = v
}

// GetSlackDestination returns the SlackDestination field value if set, zero value otherwise.
func (o *Action) GetSlackDestination() ActionSlackDestination {
	if o == nil || IsNil(o.SlackDestination) {
		var ret ActionSlackDestination
		return ret
	}
	return *o.SlackDestination
}

// GetSlackDestinationOk returns a tuple with the SlackDestination field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Action) GetSlackDestinationOk() (*ActionSlackDestination, bool) {
	if o == nil || IsNil(o.SlackDestination) {
		return nil, false
	}
	return o.SlackDestination, true
}

// HasSlackDestination returns a boolean if a field has been set.
func (o *Action) HasSlackDestination() bool {
	if o != nil && !IsNil(o.SlackDestination) {
		return true
	}

	return false
}

// SetSlackDestination gets a reference to the given ActionSlackDestination and assigns it to the SlackDestination field.
func (o *Action) SetSlackDestination(v ActionSlackDestination) {
	o.SlackDestination = &v
}

// GetPagerDutyDestination returns the PagerDutyDestination field value if set, zero value otherwise.
func (o *Action) GetPagerDutyDestination() ActionPagerDutyDestination {
	if o == nil || IsNil(o.PagerDutyDestination) {
		var ret ActionPagerDutyDestination
		return ret
	}
	return *o.PagerDutyDestination
}

// GetPagerDutyDestinationOk returns a tuple with the PagerDutyDestination field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Action) GetPagerDutyDestinationOk() (*ActionPagerDutyDestination, bool) {
	if o == nil || IsNil(o.PagerDutyDestination) {
		return nil, false
	}
	return o.PagerDutyDestination, true
}

// HasPagerDutyDestination returns a boolean if a field has been set.
func (o *Action) HasPagerDutyDestination() bool {
	if o != nil && !IsNil(o.PagerDutyDestination) {
		return true
	}

	return false
}

// SetPagerDutyDestination gets a reference to the given ActionPagerDutyDestination and assigns it to the PagerDutyDestination field.
func (o *Action) SetPagerDutyDestination(v ActionPagerDutyDestination) {
	o.PagerDutyDestination = &v
}

// GetEmailDestination returns the EmailDestination field value if set, zero value otherwise.
func (o *Action) GetEmailDestination() ActionEmailDestination {
	if o == nil || IsNil(o.EmailDestination) {
		var ret ActionEmailDestination
		return ret
	}
	return *o.EmailDestination
}

// GetEmailDestinationOk returns a tuple with the EmailDestination field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Action) GetEmailDestinationOk() (*ActionEmailDestination, bool) {
	if o == nil || IsNil(o.EmailDestination) {
		return nil, false
	}
	return o.EmailDestination, true
}

// HasEmailDestination returns a boolean if a field has been set.
func (o *Action) HasEmailDestination() bool {
	if o != nil && !IsNil(o.EmailDestination) {
		return true
	}

	return false
}

// SetEmailDestination gets a reference to the given ActionEmailDestination and assigns it to the EmailDestination field.
func (o *Action) SetEmailDestination(v ActionEmailDestination) {
	o.EmailDestination = &v
}

// GetWebhookDestination returns the WebhookDestination field value if set, zero value otherwise.
func (o *Action) GetWebhookDestination() ActionWebhookDestination {
	if o == nil || IsNil(o.WebhookDestination) {
		var ret ActionWebhookDestination
		return ret
	}
	return *o.WebhookDestination
}

// GetWebhookDestinationOk returns a tuple with the WebhookDestination field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Action) GetWebhookDestinationOk() (*ActionWebhookDestination, bool) {
	if o == nil || IsNil(o.WebhookDestination) {
		return nil, false
	}
	return o.WebhookDestination, true
}

// HasWebhookDestination returns a boolean if a field has been set.
func (o *Action) HasWebhookDestination() bool {
	if o != nil && !IsNil(o.WebhookDestination) {
		return true
	}

	return false
}

// SetWebhookDestination gets a reference to the given ActionWebhookDestination and assigns it to the WebhookDestination field.
func (o *Action) SetWebhookDestination(v ActionWebhookDestination) {
	o.WebhookDestination = &v
}

func (o Action) MarshalJSON() ([]byte, error) {
	toSerialize, err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o Action) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["type"] = o.Type
	if !IsNil(o.SlackDestination) {
		toSerialize["slackDestination"] = o.SlackDestination
	}
	if !IsNil(o.PagerDutyDestination) {
		toSerialize["pagerDutyDestination"] = o.PagerDutyDestination
	}
	if !IsNil(o.EmailDestination) {
		toSerialize["emailDestination"] = o.EmailDestination
	}
	if !IsNil(o.WebhookDestination) {
		toSerialize["webhookDestination"] = o.WebhookDestination
	}
	return toSerialize, nil
}

func (o *Action) UnmarshalJSON(data []byte) (err error) {
	// This validates that all required properties are included in the JSON object
	// by unmarshalling the object into a generic map with string keys and checking
	// that every required field exists as a key in the generic map.
	requiredProperties := []string{
		"type",
	}

	allProperties := make(map[string]interface{})

	err = json.Unmarshal(data, &allProperties)

	if err != nil {
		return err
	}

	for _, requiredProperty := range requiredProperties {
		if _, exists := allProperties[requiredProperty]; !exists {
			return fmt.Errorf("no value given for required property %v", requiredProperty)
		}
	}

	varAction := _Action{}

	decoder := json.NewDecoder(bytes.NewReader(data))
	decoder.DisallowUnknownFields()
	err = decoder.Decode(&varAction)

	if err != nil {
		return err
	}

	*o = Action(varAction)

	return err
}

type NullableAction struct {
	value *Action
	isSet bool
}

func (v NullableAction) Get() *Action {
	return v.value
}

func (v *NullableAction) Set(val *Action) {
	v.value = val
	v.isSet = true
}

func (v NullableAction) IsSet() bool {
	return v.isSet
}

func (v *NullableAction) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableAction(val *Action) *NullableAction {
	return &NullableAction{value: val, isSet: true}
}

func (v NullableAction) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableAction) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}

```

--------------------------------------------------------------------------------
/tools/create_alert.go:
--------------------------------------------------------------------------------

```go
package tools

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/uuid"
	mcpgolang "github.com/metoro-io/mcp-golang"
	"github.com/metoro-io/metoro-mcp-server/model"
	"github.com/metoro-io/metoro-mcp-server/utils"
)

type CreateAlertHandlerArgs struct {
	AlertName         string                  `json:"alert_name" jsonschema:"required,description=The name of the alert to create"`
	AlertDescription  string                  `json:"alert_description" jsonschema:"required,description=The description of the alert to create"`
	Timeseries        []model.MetricSpecifier `json:"timeseries" jsonschema:"required,description=Array of timeseries data to get. Each item in this array corresponds to a single timeseries. You can then use the formulas to combine these timeseries. If you only want to see the combination of timeseries via defining formulas and if you dont want to see the individual timeseries data when setting formulas you can set shouldNotReturn to true. For each timeseries make sure to set the type."`
	Formula           model.Formula           `json:"formula" jsonschema:"description=Optional formula to combine timeseries. Formula should only consist of formulaIdentifier of the timeseries in the timeseries array. e.g. a + b + c if a b c appears in the formulaIdentifier of the timeseries array. You can ONLY do the following operations: Arithmetic operations:+ (for add) - (for substract) * (for multiply) / (for division) % (for modulus) ^ or ** (for exponent). Comparison: == != < > <= >= . Logical:! (for not) && (for AND) || (for OR). Conditional operations: ?: (ternary) e.g. (a || b) ? 1 : 0. Do not guess the operations. Just use these available ones!"`
	Condition         string                  `json:"condition" jsonschema:"required,enum=GreaterThan,enum=LessThan,enum=GreaterThanOrEqual,enum=LessThanOrEqual,description=the arithmetic comparison to use to evaluate whether an alert is firing or not. This is used to determine whether the alert should be triggered based on the threshold value."`
	Threshold         float64                 `json:"threshold" jsonschema:"required,description=The threshold value for the alert. This is the value that will be used together with the the arithmetic condition to see whether the alert should be triggered or not. For example if you set the condition to GreaterThan and the threshold to 100 then the alert will fire if the value of the timeseries is greater than 100."`
	DatapointsToAlarm int64                   `json:"datapoints_to_alarm" jsonschema:"required,description=The number of datapoints that need to breach the threshold for the alert to be triggered"`
	EvaluationWindow  int64                   `json:"evaluation_window" jsonschema:"required,description=The evaluation window in number of datapoints. This is the number of datapoints that will be considered for evaluating the alert condition. For example if you set this to then the last 5 datapoints will be considered for evaluating the alert condition. This is useful for smoothing out spikes in the data and preventing false positives."`
}

func CreateAlertHandler(ctx context.Context, arguments CreateAlertHandlerArgs) (*mcpgolang.ToolResponse, error) {
	alert, err := createAlertFromTimeseries(ctx, arguments.AlertName, arguments.AlertDescription, arguments.Timeseries, arguments.Formula, arguments.Condition, arguments.Threshold, arguments.DatapointsToAlarm, arguments.EvaluationWindow)
	if err != nil {
		return nil, fmt.Errorf("error creating alert properties: %v", err)
	}

	newAlertRequest := model.CreateUpdateAlertRequest{
		Alert: alert,
	}

	resp, err := setAlertMetoroCall(ctx, newAlertRequest)
	if err != nil {
		return nil, fmt.Errorf("error setting dashboard: %v", err)
	}
	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

// TODO: Implement the conversion logic.
func createAlertFromTimeseries(ctx context.Context, alertName, alertDescription string, timeseries []model.MetricSpecifier, formula model.Formula, condition string, threshold float64, datapointsToAlarm int64, evaluationWindow int64) (model.Alert, error) {
	// Create dummy time range for the last 10 minutes to validate the timeseries
	endTime := time.Now().Unix()
	startTime := endTime - 600 // 10 minutes ago

	// Convert MetricSpecifier to SingleTimeseriesRequest for validation
	singleTimeseriesRequests := convertMetricSpecifierToSingleTimeseries(timeseries)

	err := checkTimeseries(ctx, singleTimeseriesRequests, startTime, endTime)
	if err != nil {
		return model.Alert{}, err
	}
	metoroQlQueries, err := convertMetricSpecifierToMetoroQL(ctx, timeseries, []model.Formula{formula})
	if err != nil {
		return model.Alert{}, fmt.Errorf("error converting metric specifiers to MetoroQL: %v", err)
	}

	// Convert condition string to OperatorType
	var operatorType model.OperatorType
	switch condition {
	case "GreaterThan":
		operatorType = model.GREATER_THAN
	case "LessThan":
		operatorType = model.LESS_THAN
	case "GreaterThanOrEqual":
		operatorType = model.GREATER_THAN_OR_EQUAL
	case "LessThanOrEqual":
		operatorType = model.LESS_THAN_OR_EQUAL
	default:
		return model.Alert{}, fmt.Errorf("invalid condition: %s", condition)
	}

	// Determine bucket size from the timeseries
	bucketSize := int64(60) // default to 60 seconds
	if len(timeseries) > 0 && timeseries[0].BucketSize > 0 {
		bucketSize = timeseries[0].BucketSize
	}

	// Use the first MetoroQL query (usually the combined formula result)
	query := ""
	if len(metoroQlQueries) > 0 {
		for _, q := range metoroQlQueries {
			if q != "" {
				query = q
				break
			}
		}
	}

	// Create the alert
	conditionType := model.STATIC
	timeseriesType := model.TIMESERIES
	alert := model.Alert{
		Metadata: model.MetadataObject{
			Name:        alertName,
			Description: &alertDescription,
			Id:          uuid.NewString(),
		},
		Type: &timeseriesType,
		Timeseries: model.TimeseriesConfig{
			Expression: model.ExpressionConfig{
				MetoroQLTimeseries: &model.MetoroQlTimeseries{
					Query:      query,
					BucketSize: bucketSize,
				},
			},
			EvaluationRules: []model.Condition{
				{
					Name: "Alert Condition",
					Type: &conditionType,
					Static: &model.StaticCondition{
						Operators: []model.OperatorConfig{
							{
								Operator:  operatorType,
								Threshold: threshold,
							},
						},
						PersistenceSettings: model.PersistenceSettings{
							DatapointsToAlarm:            datapointsToAlarm,
							DatapointsInEvaluationWindow: evaluationWindow,
						},
					},
				},
			},
		},
	}

	return alert, nil
}

func convertMetricSpecifierToMetoroQL(ctx context.Context, metricSpecs []model.MetricSpecifier, formulas []model.Formula) ([]string, error) {
	req := model.MetricSpecifiersRequest{
		MetricSpecifiers: metricSpecs,
		Formulas:         formulas,
	}
	requestBody, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("error marshaling MetricSpecifiersRequest: %v", err)
	}
	resp, err := utils.MakeMetoroAPIRequest("POST", "metoroql/convert/metricSpecifierToMetoroql", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, fmt.Errorf("error making MetoroQL conversion request: %v", err)
	}
	var metoroQLQueriesResp model.MetricSpecifierToMetoroQLResponse
	if err := json.Unmarshal(resp, &metoroQLQueriesResp); err != nil {
		return nil, fmt.Errorf("error unmarshaling MetoroQL conversion response: %v", err)
	}
	if len(metoroQLQueriesResp.Queries) == 0 {
		return nil, fmt.Errorf("no MetoroQL queries returned from conversion")
	}
	return metoroQLQueriesResp.Queries, nil
}

func setAlertMetoroCall(ctx context.Context, request model.CreateUpdateAlertRequest) ([]byte, error) {
	requestBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling alert request: %v", err)
	}
	return utils.MakeMetoroAPIRequest("POST", "alerts/update", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
}

// convertMetricSpecifierToSingleTimeseries converts MetricSpecifier to SingleTimeseriesRequest
func convertMetricSpecifierToSingleTimeseries(metricSpecs []model.MetricSpecifier) []model.SingleTimeseriesRequest {
	result := make([]model.SingleTimeseriesRequest, len(metricSpecs))
	for i, spec := range metricSpecs {
		result[i] = model.SingleTimeseriesRequest{
			Type:              spec.MetricType,
			MetricName:        spec.MetricName,
			Aggregation:       spec.Aggregation,
			Filters:           spec.Filters,
			ExcludeFilters:    spec.ExcludeFilters,
			Splits:            spec.Splits,
			Regexes:           spec.Regexes,
			ExcludeRegexes:    spec.ExcludeRegexes,
			BucketSize:        spec.BucketSize,
			Functions:         spec.Functions,
			ShouldNotReturn:   spec.ShouldNotReturn,
			FormulaIdentifier: "",
		}
	}
	return result
}

```

--------------------------------------------------------------------------------
/model/model_timeseries_specifier.go:
--------------------------------------------------------------------------------

```go
/*
Metoro Alerts API

API for managing alerts in the Metoro observability platform.

API version: 1.0.0
*/

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.

package model

import (
	"encoding/json"
	"bytes"
	"fmt"
)

// checks if the TimeseriesSpecifier type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &TimeseriesSpecifier{}

// TimeseriesSpecifier Specification for a timeseries
type TimeseriesSpecifier struct {
	// Type of timeseries
	Type string `json:"type"`
	Metric *TimeseriesSpecifierMetric `json:"metric,omitempty"`
	KubernetesResource *TimeseriesSpecifierKubernetesResource `json:"kubernetes_resource,omitempty"`
	Logs *TimeseriesSpecifierLogs `json:"logs,omitempty"`
	Traces *TimeseriesSpecifierTraces `json:"traces,omitempty"`
	// Filters to apply to the timeseries
	Filters []TimeseriesSpecifierFiltersInner `json:"filters,omitempty"`
	// Aggregation function to apply
	Aggregation *string `json:"aggregation,omitempty"`
	// Size of buckets in seconds for data aggregation
	BucketSize *int64 `json:"bucketSize,omitempty"`
	// Fields to split the results by
	Splits []string `json:"splits,omitempty"`
	// Functions to apply to the timeseries
	Functions []TimeseriesSpecifierFunction `json:"functions,omitempty"`
}

type _TimeseriesSpecifier TimeseriesSpecifier

// NewTimeseriesSpecifier instantiates a new TimeseriesSpecifier object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewTimeseriesSpecifier(type_ string) *TimeseriesSpecifier {
	this := TimeseriesSpecifier{}
	this.Type = type_
	return &this
}

// NewTimeseriesSpecifierWithDefaults instantiates a new TimeseriesSpecifier object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewTimeseriesSpecifierWithDefaults() *TimeseriesSpecifier {
	this := TimeseriesSpecifier{}
	return &this
}

// GetType returns the Type field value
func (o *TimeseriesSpecifier) GetType() string {
	if o == nil {
		var ret string
		return ret
	}

	return o.Type
}

// GetTypeOk returns a tuple with the Type field value
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifier) GetTypeOk() (*string, bool) {
	if o == nil {
		return nil, false
	}
	return &o.Type, true
}

// SetType sets field value
func (o *TimeseriesSpecifier) SetType(v string) {
	o.Type = v
}

// GetMetric returns the Metric field value if set, zero value otherwise.
func (o *TimeseriesSpecifier) GetMetric() TimeseriesSpecifierMetric {
	if o == nil || IsNil(o.Metric) {
		var ret TimeseriesSpecifierMetric
		return ret
	}
	return *o.Metric
}

// GetMetricOk returns a tuple with the Metric field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifier) GetMetricOk() (*TimeseriesSpecifierMetric, bool) {
	if o == nil || IsNil(o.Metric) {
		return nil, false
	}
	return o.Metric, true
}

// HasMetric returns a boolean if a field has been set.
func (o *TimeseriesSpecifier) HasMetric() bool {
	if o != nil && !IsNil(o.Metric) {
		return true
	}

	return false
}

// SetMetric gets a reference to the given TimeseriesSpecifierMetric and assigns it to the Metric field.
func (o *TimeseriesSpecifier) SetMetric(v TimeseriesSpecifierMetric) {
	o.Metric = &v
}

// GetKubernetesResource returns the KubernetesResource field value if set, zero value otherwise.
func (o *TimeseriesSpecifier) GetKubernetesResource() TimeseriesSpecifierKubernetesResource {
	if o == nil || IsNil(o.KubernetesResource) {
		var ret TimeseriesSpecifierKubernetesResource
		return ret
	}
	return *o.KubernetesResource
}

// GetKubernetesResourceOk returns a tuple with the KubernetesResource field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifier) GetKubernetesResourceOk() (*TimeseriesSpecifierKubernetesResource, bool) {
	if o == nil || IsNil(o.KubernetesResource) {
		return nil, false
	}
	return o.KubernetesResource, true
}

// HasKubernetesResource returns a boolean if a field has been set.
func (o *TimeseriesSpecifier) HasKubernetesResource() bool {
	if o != nil && !IsNil(o.KubernetesResource) {
		return true
	}

	return false
}

// SetKubernetesResource gets a reference to the given TimeseriesSpecifierKubernetesResource and assigns it to the KubernetesResource field.
func (o *TimeseriesSpecifier) SetKubernetesResource(v TimeseriesSpecifierKubernetesResource) {
	o.KubernetesResource = &v
}

// GetLogs returns the Logs field value if set, zero value otherwise.
func (o *TimeseriesSpecifier) GetLogs() TimeseriesSpecifierLogs {
	if o == nil || IsNil(o.Logs) {
		var ret TimeseriesSpecifierLogs
		return ret
	}
	return *o.Logs
}

// GetLogsOk returns a tuple with the Logs field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifier) GetLogsOk() (*TimeseriesSpecifierLogs, bool) {
	if o == nil || IsNil(o.Logs) {
		return nil, false
	}
	return o.Logs, true
}

// HasLogs returns a boolean if a field has been set.
func (o *TimeseriesSpecifier) HasLogs() bool {
	if o != nil && !IsNil(o.Logs) {
		return true
	}

	return false
}

// SetLogs gets a reference to the given TimeseriesSpecifierLogs and assigns it to the Logs field.
func (o *TimeseriesSpecifier) SetLogs(v TimeseriesSpecifierLogs) {
	o.Logs = &v
}

// GetTraces returns the Traces field value if set, zero value otherwise.
func (o *TimeseriesSpecifier) GetTraces() TimeseriesSpecifierTraces {
	if o == nil || IsNil(o.Traces) {
		var ret TimeseriesSpecifierTraces
		return ret
	}
	return *o.Traces
}

// GetTracesOk returns a tuple with the Traces field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifier) GetTracesOk() (*TimeseriesSpecifierTraces, bool) {
	if o == nil || IsNil(o.Traces) {
		return nil, false
	}
	return o.Traces, true
}

// HasTraces returns a boolean if a field has been set.
func (o *TimeseriesSpecifier) HasTraces() bool {
	if o != nil && !IsNil(o.Traces) {
		return true
	}

	return false
}

// SetTraces gets a reference to the given TimeseriesSpecifierTraces and assigns it to the Traces field.
func (o *TimeseriesSpecifier) SetTraces(v TimeseriesSpecifierTraces) {
	o.Traces = &v
}

// GetFilters returns the Filters field value if set, zero value otherwise.
func (o *TimeseriesSpecifier) GetFilters() []TimeseriesSpecifierFiltersInner {
	if o == nil || IsNil(o.Filters) {
		var ret []TimeseriesSpecifierFiltersInner
		return ret
	}
	return o.Filters
}

// GetFiltersOk returns a tuple with the Filters field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifier) GetFiltersOk() ([]TimeseriesSpecifierFiltersInner, bool) {
	if o == nil || IsNil(o.Filters) {
		return nil, false
	}
	return o.Filters, true
}

// HasFilters returns a boolean if a field has been set.
func (o *TimeseriesSpecifier) HasFilters() bool {
	if o != nil && !IsNil(o.Filters) {
		return true
	}

	return false
}

// SetFilters gets a reference to the given []TimeseriesSpecifierFiltersInner and assigns it to the Filters field.
func (o *TimeseriesSpecifier) SetFilters(v []TimeseriesSpecifierFiltersInner) {
	o.Filters = v
}

// GetAggregation returns the Aggregation field value if set, zero value otherwise.
func (o *TimeseriesSpecifier) GetAggregation() string {
	if o == nil || IsNil(o.Aggregation) {
		var ret string
		return ret
	}
	return *o.Aggregation
}

// GetAggregationOk returns a tuple with the Aggregation field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifier) GetAggregationOk() (*string, bool) {
	if o == nil || IsNil(o.Aggregation) {
		return nil, false
	}
	return o.Aggregation, true
}

// HasAggregation returns a boolean if a field has been set.
func (o *TimeseriesSpecifier) HasAggregation() bool {
	if o != nil && !IsNil(o.Aggregation) {
		return true
	}

	return false
}

// SetAggregation gets a reference to the given string and assigns it to the Aggregation field.
func (o *TimeseriesSpecifier) SetAggregation(v string) {
	o.Aggregation = &v
}

// GetBucketSize returns the BucketSize field value if set, zero value otherwise.
func (o *TimeseriesSpecifier) GetBucketSize() int64 {
	if o == nil || IsNil(o.BucketSize) {
		var ret int64
		return ret
	}
	return *o.BucketSize
}

// GetBucketSizeOk returns a tuple with the BucketSize field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifier) GetBucketSizeOk() (*int64, bool) {
	if o == nil || IsNil(o.BucketSize) {
		return nil, false
	}
	return o.BucketSize, true
}

// HasBucketSize returns a boolean if a field has been set.
func (o *TimeseriesSpecifier) HasBucketSize() bool {
	if o != nil && !IsNil(o.BucketSize) {
		return true
	}

	return false
}

// SetBucketSize gets a reference to the given int64 and assigns it to the BucketSize field.
func (o *TimeseriesSpecifier) SetBucketSize(v int64) {
	o.BucketSize = &v
}

// GetSplits returns the Splits field value if set, zero value otherwise.
func (o *TimeseriesSpecifier) GetSplits() []string {
	if o == nil || IsNil(o.Splits) {
		var ret []string
		return ret
	}
	return o.Splits
}

// GetSplitsOk returns a tuple with the Splits field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifier) GetSplitsOk() ([]string, bool) {
	if o == nil || IsNil(o.Splits) {
		return nil, false
	}
	return o.Splits, true
}

// HasSplits returns a boolean if a field has been set.
func (o *TimeseriesSpecifier) HasSplits() bool {
	if o != nil && !IsNil(o.Splits) {
		return true
	}

	return false
}

// SetSplits gets a reference to the given []string and assigns it to the Splits field.
func (o *TimeseriesSpecifier) SetSplits(v []string) {
	o.Splits = v
}

// GetFunctions returns the Functions field value if set, zero value otherwise.
func (o *TimeseriesSpecifier) GetFunctions() []TimeseriesSpecifierFunction {
	if o == nil || IsNil(o.Functions) {
		var ret []TimeseriesSpecifierFunction
		return ret
	}
	return o.Functions
}

// GetFunctionsOk returns a tuple with the Functions field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *TimeseriesSpecifier) GetFunctionsOk() ([]TimeseriesSpecifierFunction, bool) {
	if o == nil || IsNil(o.Functions) {
		return nil, false
	}
	return o.Functions, true
}

// HasFunctions returns a boolean if a field has been set.
func (o *TimeseriesSpecifier) HasFunctions() bool {
	if o != nil && !IsNil(o.Functions) {
		return true
	}

	return false
}

// SetFunctions gets a reference to the given []TimeseriesSpecifierFunction and assigns it to the Functions field.
func (o *TimeseriesSpecifier) SetFunctions(v []TimeseriesSpecifierFunction) {
	o.Functions = v
}

func (o TimeseriesSpecifier) MarshalJSON() ([]byte, error) {
	toSerialize,err := o.ToMap()
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(toSerialize)
}

func (o TimeseriesSpecifier) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["type"] = o.Type
	if !IsNil(o.Metric) {
		toSerialize["metric"] = o.Metric
	}
	if !IsNil(o.KubernetesResource) {
		toSerialize["kubernetes_resource"] = o.KubernetesResource
	}
	if !IsNil(o.Logs) {
		toSerialize["logs"] = o.Logs
	}
	if !IsNil(o.Traces) {
		toSerialize["traces"] = o.Traces
	}
	if !IsNil(o.Filters) {
		toSerialize["filters"] = o.Filters
	}
	if !IsNil(o.Aggregation) {
		toSerialize["aggregation"] = o.Aggregation
	}
	if !IsNil(o.BucketSize) {
		toSerialize["bucketSize"] = o.BucketSize
	}
	if !IsNil(o.Splits) {
		toSerialize["splits"] = o.Splits
	}
	if !IsNil(o.Functions) {
		toSerialize["functions"] = o.Functions
	}
	return toSerialize, nil
}

func (o *TimeseriesSpecifier) UnmarshalJSON(data []byte) (err error) {
	// This validates that all required properties are included in the JSON object
	// by unmarshalling the object into a generic map with string keys and checking
	// that every required field exists as a key in the generic map.
	requiredProperties := []string{
		"type",
	}

	allProperties := make(map[string]interface{})

	err = json.Unmarshal(data, &allProperties)

	if err != nil {
		return err;
	}

	for _, requiredProperty := range(requiredProperties) {
		if _, exists := allProperties[requiredProperty]; !exists {
			return fmt.Errorf("no value given for required property %v", requiredProperty)
		}
	}

	varTimeseriesSpecifier := _TimeseriesSpecifier{}

	decoder := json.NewDecoder(bytes.NewReader(data))
	decoder.DisallowUnknownFields()
	err = decoder.Decode(&varTimeseriesSpecifier)

	if err != nil {
		return err
	}

	*o = TimeseriesSpecifier(varTimeseriesSpecifier)

	return err
}

type NullableTimeseriesSpecifier struct {
	value *TimeseriesSpecifier
	isSet bool
}

func (v NullableTimeseriesSpecifier) Get() *TimeseriesSpecifier {
	return v.value
}

func (v *NullableTimeseriesSpecifier) Set(val *TimeseriesSpecifier) {
	v.value = val
	v.isSet = true
}

func (v NullableTimeseriesSpecifier) IsSet() bool {
	return v.isSet
}

func (v *NullableTimeseriesSpecifier) Unset() {
	v.value = nil
	v.isSet = false
}

func NewNullableTimeseriesSpecifier(val *TimeseriesSpecifier) *NullableTimeseriesSpecifier {
	return &NullableTimeseriesSpecifier{value: val, isSet: true}
}

func (v NullableTimeseriesSpecifier) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.value)
}

func (v *NullableTimeseriesSpecifier) UnmarshalJSON(src []byte) error {
	v.isSet = true
	return json.Unmarshal(src, &v.value)
}



```

--------------------------------------------------------------------------------
/tools/tools.go:
--------------------------------------------------------------------------------

```go
package tools

type MetoroTools struct {
	Name        string
	Description string
	Handler     any
}

var MetoroToolsList = []MetoroTools{
	{
		Name:        "get_environments",
		Description: "Get Kubernetes environments/clusters. This tool is useful for listing the kubernetes environments/clusters that are monitored by Metoro.",
		Handler:     GetEnvironmentsHandler,
	},
	{
		Name:        "get_services",
		Description: "Get services running in your Kubernetes cluster. Metoro treats the following Kubernetes resources as a 'service': Deployment, StatefulSet, DaemonSet. This tool is useful for listing the services/workloads running in your Kubernetes cluster.",
		Handler:     GetServicesHandler,
	},
	{
		Name:        "get_namespaces",
		Description: "Get namespaces in your Kubernetes cluster. This tool is useful for listing the namespaces in your Kubernetes cluster.",
		Handler:     GetNamespacesHandler,
	},
	{
		Name:        "get_logs",
		Description: `Get logs from all or specific services/hosts/pods. Results are limited to 20 logs lines.  Before using this you MUST first call get_attribute_keys and get_attribute_values to get the possible log attribute keys and values which can be used as Filter/ExcludeFilter keys.`,
		Handler:     GetLogsHandler,
	},
	{
		Name: "get_traces",
		Description: `Get list of traces from your cluster. Results are limited to 20 traces so try to use filters to narrow down what you are looking for.
                      Prior to using this tool, YOU MUST first call get_attribute_keys and subsequently get_attribute_values to get the possible trace attribute keys and values which can be used as Filter/ExcludeFilter keys.
					  Use this tool when you are interested in the trace attributes to get more information to answer why/what. If you want more details about a specific trace use get_trace_spans to see individual span details.
                      If you would like to check existence of traces use get_timeseries_data tool with type=trace to get count/p50/p90/p95/p99 of traces instead of using get_traces tool.
                      After calling get traces you should normally call get_trace_spans to get the spans associated with the traceId you are interested in. When reading duration of a trace use the durationReadable field.`,
		Handler: GetTracesHandler,
	},
	{
		Name:        "get_trace_spans",
		Description: `Get the spans associated with a specific traceId. This allows you to view the entire trace with all its spans in a tree like structure. You should basically always use this after calling get_traces tool to get the traceId you are interested in. This tool gives you all spans in a trace.`,
		Handler:     GetTraceSpansHandler,
	},
	{
		Name: "get_traces_distribution",
		Description: `Gets the most common attribute - value pairs for the traces matching the filters. 
                      The provided filters are the same as the filters used in the get_traces tool. 
                      You should use this tool to understand if there are any common attributes in traces that you are interested in. 
                      For example if you have some failing traces you should use this tool and it might tell you that they are all on a specific path or coming from a specific pod or environment. 
                      This is a very useful tool and should often be used before calling get_traces.
                      Prior to using this tool, YOU MUST first call get_attribute_keys and subsequently get_attribute_values to get the possible trace attribute keys and values which can be used as Filter/ExcludeFilter keys.
`,
		Handler: GetTracesDistributionHandler,
	},
	{
		Name: "get_timeseries_data",
		Description: `Get one or more timeseries data for a metric or traces or logs or kubernetes resources. This tool is useful for understanding how the underlying type of data (specific/metric/trace/kubernetes resources/logs) change over time. You can also apply formulas to combine timeseries to calculate rates or ratios or differences etc. How to use this tool:
					  First you need the type of timeseries data you are requesting for. This can be one of metric or traces or logs or kubernetes resources. If it is metrics then you HAVE TO call the get_metric_names tool to get the available metric names which can be used as MetricName argument for this tool.
					  Then YOU HAVE TO call get_attribute_keys tool to retrieve the available attribute keys and get_attribute_values to retrieve values you are interested in to use in Filter/ExcludeFilter keys for this tool.
					  You can also use Splits argument to group/split the metric data by the given metric attribute keys. Only use the attribute keys and values that are available for the MetricName that are returned from get_attribute_keys and get_attribute_values tools. If you are not getting proper results back then you might have forgotten to set the correct attribute keys and values. Try again with the correct attribute keys and values you get from get_attribute_values.
                      Metrics of type counter (or with _total suffix) are cumulative metrics but Metoro querying engine already accounts for rate differences when returning the value so you don't need to calculate the rate/monotonic difference yourself. You can just query those metrics as they are without extra functions. If you are in doubt use the get_metric_metadata tool to get more information (description type unit) about the metric and how to use it.
`,
		Handler: GetMultiMetricHandler,
	},
	{
		Name: "get_attribute_keys",
		Description: `Get the possible attribute keys for a specific type of data. This tool is useful for understanding the possible attribute keys that can be used for filtering the data. How to use this tool:
					  First you need the type of data you are requesting for. This can be one of metric or traces or logs or kubernetes resources.
					  Then you can call this tool to get the possible attribute keys for the given type of data.`,
		Handler: GetAttributeKeysHandler,
	},
	{
		Name: "get_attribute_values",
		Description: `"Get the possible values of an attribute key for a given type of data which can be one of metric trace logs or kubernetes_resource. This can be used as a value for a filtering key for filtering data. How to use this tool:
					  First you need the type of data you are requesting for. This can be one of metric or traces or logs or kubernetes resources. Then you need the attribute keys for the given type of data. You can use get_attribute_keys tool to get the available attribute keys for the given type of data.
					  Then you can call this tool to get the possible values for a given attribute key for the given type of data. If you want to get the possible values for a metric attribute key you can use the get_metric_names tool to get the available metric names which can be used as MetricName argument for this tool and then use get_attribute_keys tool to get the available attribute keys and get_attribute_values to get values for the key which can be used as Filter/ExcludeFilter keys for`,
		Handler: GetAttributeValuesHandler,
	},
	{
		Name:        "get_profiles",
		Description: "Get cpu profiles of your services running in your Kubernetes cluster. This tool is useful for answering performance related questions for a specific service. It provides information about which functions taking time in the service.",
		Handler:     GetProfilesHandler,
	},
	{
		Name: "get_k8s_events",
		Description: `Get the Kubernetes events from your clusters. Kubernetes events are useful for understanding what is happening with regards to your Kubernetes resources.
They are emitted by the Kubernetes API server when there is a change in the state of the cluster. How to use this tool:
First use get_k8s_events_attributes tool to retrieve the available Kubernetes event attribute keys which can be used as Filter/ExcludeFilter keys for this tool.
Then use get_k8s_event_attribute_values_for_individual_attribute tool to get the possible values a Kubernetes event attribute key can be for filtering Kubernetes events.
And then you can call this tool (get_k8s_events) to get the specific events you are looking for. e.g. Filter use case: get_k8s_events with filters: {key: [value]} for including specific Kubernetes events.`,
		Handler: GetK8sEventsHandler,
	},
	{
		Name:        "get_k8s_events_attributes",
		Description: "Get possible attribute keys for Kubernetes events which can be used for filtering them.",
		Handler:     GetK8sEventsAttributesHandler,
	},
	{
		Name:        "get_k8s_event_attribute_values_for_individual_attribute",
		Description: "Get possible attribute values for a specific Kubernetes event attribute key. E.g. EventType attribute key might have values like Normal Warning etc.",
		Handler:     GetK8sEventAttributeValuesForIndividualAttributeHandler,
	},
	{
		Name:        "get_metric_names",
		Description: "Get available metric names to query. These metric names can be used as MetricName argument for get_metric get_metric_metadata and get_timeseries_data and get_attribute_keys tools.",
		Handler:     GetMetricNamesHandler,
	},
	{
		Name:        "get_metric_metadata",
		Description: "Get metric description and type and unit for a metric. This tool can be used to get detailed information about a metric including its type unit and description. Use this tool after getting the metric name that you are interested in from the get_metric_names tool and before calling the get_timeseries_data tool to understand the metric better.",
		Handler:     GetMetricMetadata,
	},
	//{
	//	Name:        "get_pods",
	//	Description: "Get the list of pods that are running in your cluster. This tool is useful for getting the name of the pods. You must provide either a ServiceName to get pods for a specific service or a NodeName to get pods running on a specific node.",
	//	Handler:     GetPodsHandler,
	//},
	{
		Name:        "get_service_yaml",
		Description: "Returns environment and YAML of a kubernetes resource/service. This tool is useful for understanding the YAML configuration of a service.",
		Handler:     GetK8sServiceInformationHandler,
	},
	{
		Name:        "get_version_for_service",
		Description: "Get container IDs and their image versions for a specific service. This tool extracts container names and their image versions from the service YAML configuration.",
		Handler:     GetVersionForServiceHandler,
	},
	{
		Name:        "get_nodes",
		Description: "Get the nodes that are running in your cluster. To use this tool first call get_node_attributes to get the possible node attribute keys and values which can be used for filtering nodes.",
		Handler:     GetNodesHandler,
	},
	{
		Name:        "get_node_attributes",
		Description: "Get possible node attribute keys and values which can be used for filtering nodes.",
		Handler:     GetNodeAttributesHandler,
	},
	{
		Name:        "get_node_info",
		Description: "Get detailed node information about a specific node. This tool provides information about the node's capacity allocatable resources and usage yaml node type OS and Kernel information.",
		Handler:     GetNodeInfoHandler,
	},
	{
		Name:        "get_service_summaries",
		Description: "Get summaries of services/workloads running in your Kubernetes cluster. The summary includes the number of requests errors (5xx and 4xx) P50 p95 p99 latencies. This tool is useful for understanding the performance of your services at a high level for a given relative or abosulute time range.",
		Handler:     GetServiceSummariesHandler,
	},
	{
		Name:        "get_alerts",
		Description: "Get list of alerts from your Kubernetes cluster. These alerts are configured by the user in Metoro therefore it may not have full coverage for all the issues that might occur in the cluster.",
		Handler:     GetAlertsHandler,
	},
	{
		Name:        "get_alert_fires",
		Description: "Get list of alert fires from your Kubernetes cluster. Alert fires are the instances when an alert is triggered. This tool provides information about the alert name the time it was triggered the time it recovered the environment and the service name (if available) and the alert trigger message.",
		Handler:     GetAlertFiresHandler,
	},
	{
		Name: "create_dashboard",
		Description: `Create a dashboard with the described metrics. This tool is useful for creating a dashboard with the metrics you are interested in.
											  How to use this tool:
					  First use get_metric_names tool to retrieve the available metric names which can be used as MetricName argument for this tool and then use get_attribute_keys tool to retrieve the available attribute keys and get_attribute_values for getting the values for the attribute key that you are interested in to use in Filter/ExcludeFilter keys or Splits argument for MetricChartWidget argument for this tool.
					  You can also use Splits argument to group the metric data by the given metric attribute keys. Only use the attribute keys and values that are available for the MetricName that are returned from get_attribute_keys and get_attribute_values tools.`,
		Handler: CreateDashboardHandler,
	},
	{
		Name: "create_alert",
		Description: `Create an alert with the described metrics. This tool is useful for creating an alert with the timeseries data that you are interested in. How to use this tool:
					 NEVER GUESS the attribute keys and values that will be used for filtering or splits. Always use trace_querier or log_querier or metric_querier to understand the available attribute keys and values for the type of data/timeseries you are interested in. Ask these tools for the available attribute keys and values and metric names etc before using this tool.`,
		Handler: CreateAlertHandler,
	},
	{
		Name:        "get_source_repository",
		Description: "Get the source repository URL/path for a specific service. This tool is useful for finding where the code for a service is stored. You need to provide the service name time range and optionally specific environments to search in.",
		Handler:     GetSourceRepositoryHandler,
	},
	{
		Name:        "get_service_graph",
		Description: "Get the service graph showing which services make calls to a given service and which services the given service makes calls to. This tool is useful for understanding service dependencies and call patterns.",
		Handler:     GetServiceGraphHandler,
	},
	{
		Name:        "unix_to_rfc3339",
		Description: "Convert a Unix timestamp (in seconds or milliseconds) to RFC3339 format. The tool automatically detects whether the timestamp is in seconds or milliseconds based on its magnitude.",
		Handler:     UnixToRFC3339Handler,
	},
	{
		Name:        "get_resources_by_ip",
		Description: "Get kubernetes resource information by IP address. This tool finds resources (like pods or services) that had a specific IP address during a given time range in a specific environment. Useful for debugging network issues or tracking pod / service history.",
		Handler:     GetResourcesByIpHandler,
	},
	{
		Name:        "create_investigation",
		Description: "Create a new investigation to document and track an issue or incident. Investigations include a title, markdown content, optional tags, and optional issue time range.",
		Handler:     CreateInvestigationHandler,
	},
	{
		Name:        "update_investigation",
		Description: "Update an existing investigation by its UUID. Allows updating the title, markdown content, time range, and other properties of an investigation.",
		Handler:     UpdateInvestigationHandler,
	},
	{
		Name:        "list_investigations",
		Description: "List investigations with optional filtering by tags and pagination. Returns a list of investigations including their title, markdown content, tags, creation/update times, and issue time ranges.",
		Handler:     ListInvestigationsHandler,
	},
	{
		Name:        "create_ai_issue",
		Description: "Create a new AI issue record with a title and markdown description. Use this to capture issues that investigations can reference via issue UUID.",
		Handler:     CreateAIIssueHandler,
	},
	{
		Name:        "update_ai_issue",
		Description: "Update an existing AI issue by UUID. Allows changing the issue title and description.",
		Handler:     UpdateAIIssueHandler,
	},
	{
		Name:        "get_ai_issue",
		Description: "Fetch a single AI issue by UUID to view its current title, description, and metadata.",
		Handler:     GetAIIssueHandler,
	},
	{
		Name:        "list_ai_issues",
		Description: "List all AI issues for the organization. Useful for discovering available issue UUIDs and their metadata.",
		Handler:     ListAIIssuesHandler,
	},
	{
		Name:        "list_ai_issue_events",
		Description: "List timeline events for a specific AI issue, including commits, releases, and investigations associated with that issue.",
		Handler:     ListAIIssueEventsHandler,
	},
}

```

--------------------------------------------------------------------------------
/model/model.go:
--------------------------------------------------------------------------------

```go
package model

import "time"

// TODO: This file should be replaced if we can import the types from Metoro repo directly.
// These are just duplicates at the moment. If updated in Metoro repository, it should also be updated here!

type GetLogsRequest struct {
	// Required: Start time of when to get the logs in seconds since epoch
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the logs in seconds since epoch
	EndTime int64 `json:"endTime"`
	// The filters to apply to the logs, so for example, if you want to get logs for a specific service
	// you can pass in a filter like {"service_name": ["microservice_a"]}
	Filters map[string][]string `json:"filters"`
	// ExcludeFilters are filters that should be excluded from the logs
	// For example, if you want to get logs for all services except microservice_a you can pass in
	// {"service_name": ["microservice_a"]}
	ExcludeFilters map[string][]string `json:"excludeFilters"`
	// Previous page endTime in nanoseconds, used to get the next page of logs if there are more logs than the page size
	// If omitted, the first page of logs will be returned
	PrevEndTime *int64 `json:"prevEndTime"`
	// Regexes are used to filter logs based on a regex inclusively
	Regexes []string `json:"regexes"`
	// ExcludeRegexes are used to filter logs based on a regex exclusively
	ExcludeRegexes []string `json:"excludeRegexes"`
	Ascending      bool     `json:"ascending"`
	// The cluster/environments to get the logs for. If empty, all clusters will be included
	Environments []string `json:"environments"`
	ExportLimit  *int     `json:"exportLimit,omitempty"` // Optional limit on the number of logs to export, defaults to 100 if not specified
}

type GetTracesRequest struct {
	ServiceNames   []string            `json:"serviceNames"`
	StartTime      int64               `json:"startTime"`
	EndTime        int64               `json:"endTime"`
	Filters        map[string][]string `json:"filters"`
	ExcludeFilters map[string][]string `json:"excludeFilters"`
	PrevEndTime    *int64              `json:"prevEndTime"`
	Regexes        []string            `json:"regexes"`
	ExcludeRegexes []string            `json:"excludeRegexes"`
	Ascending      bool                `json:"ascending"`
	Environments   []string            `json:"environments"`
	Limit          *int                `json:"limit,omitempty"` // Optional limit on the number of traces to return
}

type Aggregation string

const (
	AggregationSum   Aggregation = "sum"
	AggregationAvg   Aggregation = "avg"
	AggregationMax   Aggregation = "max"
	AggregationMin   Aggregation = "min"
	AggregationCount Aggregation = "count"
	AggregationP50   Aggregation = "p50"
	AggregationP90   Aggregation = "p90"
	AggregationP95   Aggregation = "p95"
	AggregationP99   Aggregation = "p99"

	// Only for trace metrics
	AggregationRequestSize  Aggregation = "requestSize"
	AggregationResponseSize Aggregation = "responseSize"
	AggregationTotalSize    Aggregation = "totalSize"
)

type MetricFunction struct {
	// The type of the function
	FunctionType FunctionType `json:"functionType" jsonschema:"required,enum=monotonicDifference,enum=valueDifference,enum=perSecond,description=The type of the function to apply to the metric. Do not guess the function type. Use the available ones: perSecond or valueDifference or monotonicDifference."`
	//// The payload of the function
	//// TODO: If we have more payloads this can be an interface but for now its a math expression since its the only payload.
	//FunctionPayload MathExpression `json:"functionPayload" jsonschema:"description=The payload of the customMathExpression. this is only set for customMathExpression. "`
}

type MathExpression struct {
	Variables  []string `json:"variables" jsonschema:"description=The variables to use in the math expression. For now this should always be ['a'] if set"`
	Expression string   `json:"expression" jsonschema:"description=The math expression to apply to the metric. For example if you want to divide the metric by 60 you would set the expression as a / 60"`
}

type FunctionType string

const (
	MonotonicDifference FunctionType = "monotonicDifference"
	ValueDifference     FunctionType = "valueDifference"
)

type GetMetricRequest struct {
	// MetricName is the name of the metric to get
	MetricName string `json:"metricName" jsonschema:"required,description=Name of the metric to get the timeseries data for. Do not guess the metricName, get the possible values from get_metric_names tool"`
	// Required: Start time of when to get the logs in seconds since epoch
	StartTime int64 `json:"startTime" jsonschema:"required,description=Start time of when to get the metrics in seconds since epoch"`
	// Required: End time of when to get the logs in seconds since epoch
	EndTime int64 `json:"endTime" jsonschema:"required,description=Start time of when to get the metrics in seconds since epoch"`
	// The filters to apply to the logs, so for example, if you want to get logs for a specific service
	// you can pass in a filter like {"service_name": ["microservice_a"]}
	Filters map[string][]string `json:"filters"`
	// The filters to exclude from the logs, so for example, if you want to exclude logs for a specific service
	// you can pass in a filter like {"service_name": ["microservice_a"]}
	ExcludeFilters map[string][]string `json:"excludeFilters"`
	// Splits is a list of attributes to split the metrics by, for example, if you want to split the metrics by service
	// you can pass in a list like ["service_name"]
	Splits []string `json:"splits"`
	// Aggregation is the operation to apply to the metrics, for example, if you want to sum the metrics you can pass in "sum"
	Aggregation Aggregation `json:"aggregation"`
	// IsRate is a flag to indicate if the metric is a rate metric
	IsRate bool `json:"isRate"`
	// Functions is the list of functions to apply to the metric, in the same order that they appear in this array!!
	Functions []MetricFunction `json:"functions"`
	// LimitResults is a flag to indicate if the results should be limited.
	LimitResults bool `json:"limitResults"`
	// BucketSize is the size of each datapoint bucket in seconds
	BucketSize int64 `json:"bucketSize"`
}

type MetricAttributesRequest struct {
	StartTime        int64               `json:"startTime"`
	EndTime          int64               `json:"endTime"`
	MetricName       string              `json:"metricName"`
	FilterAttributes map[string][]string `json:"filterAttributes"`
}

type FuzzyMetricsRequest struct {
	MetricFuzzyMatch string   `json:"metricFuzzyMatch"`
	Environments     []string `json:"environments"`
	StartTime        int64    `json:"startTime"`
	EndTime          int64    `json:"endTime"`
}

type GetProfileRequest struct {
	// Required: ServiceName to get profiling for
	ServiceName string `json:"serviceName"`

	// Optional: ContainerNames to get profiling for
	ContainerNames []string `json:"containerNames"`

	// Required: Timestamp to get profiling after this time
	// Seconds since epoch
	StartTime int64 `json:"startTime"`

	// Required: Timestamp to get profiling this time
	// Seconds since epoch
	EndTime int64 `json:"endTime"`
}
type GetTraceMetricRequest struct {
	// Required: Start time of when to get the logs in seconds since epoch
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the logs in seconds since epoch
	EndTime int64 `json:"endTime"`

	// Optional: The name of the service to get the trace metrics for
	// Acts as an additional filter
	ServiceNames []string `json:"serviceNames"`

	// The filters to apply to the logs, so for example, if you want to get logs for a specific service
	//you can pass in a filter like {"service_name": ["microservice_a"]}
	Filters map[string][]string `json:"filters"`

	// The exclude filters to apply to the logs, so for example, if you want to exclude logs for a specific service
	//you can pass in a filter like {"service_name": ["microservice_a"]}
	ExcludeFilters map[string][]string `json:"excludeFilters"`

	// Regexes are used to filter traces based on a regex inclusively
	Regexes []string `json:"regexes"`
	// ExcludeRegexes are used to filter traces based on a regex exclusively
	ExcludeRegexes []string `json:"excludeRegexes"`

	// Splts is a list of attributes to split the metrics by, for example, if you want to split the metrics by service
	// you can pass in a list like ["service_name"]
	Splits []string `json:"splits"`

	// Functions is the array of function to apply to the trace metrics,
	//for example, if you want to get the monotonic difference between count of traces each minute.
	// Functions are applied in the same order that they appear in this array
	Functions []MetricFunction `json:"functions"`

	// Aggregate to apply to trace metrics, for example, if you want to sum the metrics you can pass in "sum"
	Aggregate Aggregation `json:"aggregate"`

	// Environments is a list of environments to filter the traces by. If empty, all environments will be included
	Environments []string `json:"environments"`

	// LimitResults is a flag to indicate if the results should be limited.
	LimitResults bool `json:"limitResults"`

	// BucketSize is the size of each datapoint bucket in seconds
	BucketSize int64 `json:"bucketSize"`
}

type GetSingleTraceSummaryRequest struct {
	TracesSummaryRequest
	// The attribute to get the summary for
	Attribute string `json:"attribute"`
}

type TracesSummaryRequest struct {
	// Required: Start time of when to get the service summaries in seconds since epoch
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the service summaries in seconds since epoch
	EndTime int64 `json:"endTime"`

	// The filters to apply to the trace summary, so for example, if you want to get traces for a specific service
	// you can pass in a filter like {"service_name": ["microservice_a"]}
	Filters map[string][]string `json:"filters"`
	// ExcludeFilters are used to exclude traces based on a filter
	ExcludeFilters map[string][]string `json:"excludeFilters"`

	// Regexes are used to filter traces based on a regex inclusively
	Regexes []string `json:"regexes"`
	// ExcludeRegexes are used to filter traces based on a regex exclusively
	ExcludeRegexes []string `json:"excludeRegexes"`

	// Optional: The name of the service to get the trace metrics for
	// Acts as an additional filter
	ServiceNames []string `json:"serviceNames"`

	// Environments is the environments to get the traces for. If empty, all environments will be included
	Environments []string `json:"environments"`
}

type GetK8sEventsRequest struct {
	// Required: Start time of when to get the k8s events in seconds since epoch
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the k8s events in seconds since epoch
	EndTime int64 `json:"endTime"`
	// The filters to apply to the k8s events, so for example, if you want to get k8s events for a specific service
	// you can pass in a filter like {"service_name": ["microservice_a"]}
	Filters map[string][]string `json:"filters"`
	// ExcludeFilters are filters that should be excluded from the k8s events
	// For example, if you want to get k8s events for all services except microservice_a you can pass in
	// {"service_name": ["microservice_a"]}
	ExcludeFilters map[string][]string `json:"excludeFilters"`
	// Previous page endTime in nanoseconds, used to get the next page of k8s events if there are more k8s events than the page size
	// If omitted, the first page of k8s events will be returned
	PrevEndTime *int64 `json:"prevEndTime"`
	// Regexes are used to filter k8s events based on a regex inclusively
	Regexes []string `json:"regexes"`
	// ExcludeRegexes are used to filter k8s events based on a regex exclusively
	ExcludeRegexes []string `json:"excludeRegexes"`
	// Ascending is a flag to determine if the k8s events should be returned in ascending order
	Ascending bool `json:"ascending"`
	// Environments is the environments to get the k8s events for
	Environments []string `json:"environments"`
}

type GetSingleK8sEventSummaryRequest struct {
	GetK8sEventsRequest
	// The attribute to get the summary for
	Attribute string `json:"attribute"`
}

type GetK8sEventMetricsRequest struct {
	// Required: Start time of when to get the logs in seconds since epoch
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the logs in seconds since epoch
	EndTime int64 `json:"endTime"`

	// The filters to apply to the logs, so for example, if you want to get logs for a specific service
	//you can pass in a filter like {"service_name": ["microservice_a"]}
	Filters map[string][]string `json:"filters"`

	// The exclude filters to apply to the logs, so for example, if you want to exclude logs for a specific service
	//you can pass in a filter like {"service_name": ["microservice_a"]}
	ExcludeFilters map[string][]string `json:"excludeFilters"`

	// Regexes are used to filter k8s events based on a regex inclusively
	Regexes []string `json:"regexes"`
	// ExcludeRegexes are used to filter k8s events based on a regex exclusively
	ExcludeRegexes []string `json:"excludeRegexes"`

	// Splts is a list of attributes to split the metrics by, for example, if you want to split the metrics by service
	// you can pass in a list like ["service_name"]
	Splits []string `json:"splits"`

	// OnlyNumRequests is a flag to only get the number of requests, this is a much faster query
	OnlyNumRequests bool `json:"onlyNumRequests"`

	// Environments is a list of environments to filter the k8s events by. If empty, all environments will be included
	Environments []string `json:"environments"`
}

type GetPodsRequest struct {
	// Required: Timestamp to get metadata updates after this time
	StartTime int64 `json:"startTime"`

	// Required: Timestamp to get metadata updates before this time
	EndTime int64 `json:"endTime"`

	// Optional: Environment to filter the pods by. If not provided, all environments are considered
	Environments []string `json:"environments"`

	// Optional: ServiceName to get metadata updates. One of ServiceName or NodeName is required
	ServiceName string `json:"serviceName"`

	// Optional: NodeName to get metadata updates. One of ServiceName or NodeName is required
	NodeName string `json:"nodeName"`
}

type LogSummaryRequest struct {
	// Required: Start time of when to get the service summaries in seconds since epoch
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the service summaries in seconds since epoch
	EndTime int64 `json:"endTime"`
	// The filters to apply to the log summary, so for example, if you want to get logs for a specific service
	// you can pass in a filter like {"service_name": ["microservice_a"]}
	Filters map[string][]string `json:"filters"`

	ExcludeFilters map[string][]string `json:"excludeFilters"`
	// RegexFilter is a regex to filter the logs
	Regexes        []string `json:"regexes"`
	ExcludeRegexes []string `json:"excludeRegexes"`
	// The cluster/environments to get the logs for. If empty, all clusters will be included
	Environments []string `json:"environments"`
}

type GetSingleLogSummaryRequest struct {
	LogSummaryRequest
	// The attribute to get the summary for
	Attribute string `json:"attribute"`
}

type GetAllNodesRequest struct {
	// StartTime Required: Start time of when to get the nodes in seconds since epoch
	StartTime int64 `json:"startTime"`
	// EndTime Required: End time of when to get the nodes in seconds since epoch
	EndTime int64 `json:"endTime"`
	// Environments The cluster/environments to get the nodes for. If empty, all clusters will be included
	Environments []string `json:"environments"`
	// Filters The filters to apply to the nodes, so for example, if you want to get subset of nodes that have a specific label
	Filters map[string][]string `json:"filters"`
	// ExcludeFilters are filters that should be excluded from the nodes
	ExcludeFilters map[string][]string `json:"excludeFilters"`
	// Splits is a list of attributes to split the nodes by, for example, if you want to split the nodes a label
	Splits []string `json:"splits"`
}

type GetServiceSummariesRequest struct {
	// Required: Start time of when to get the service summaries in seconds
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the service summaries in seconds
	EndTime int64 `json:"endTime"`
	// If empty, all services across all environments will be returned
	Environments []string `json:"environments"`
	// Required: The namespace of the services to get summaries for. If empty, return services from all namespaces
	Namespace string `json:"namespace"`
}

// Dasboarding structs
type SetDashboardRequest struct {
	Name             string `json:"name"`
	Id               string `json:"id"`
	DashboardJson    string `json:"dashboardJson"`
	DefaultTimeRange string `json:"defaultTimeRange"`
}

// WidgetType is an enum representing different types of widgets
type WidgetType string

const (
	MetricChartWidgetType WidgetType = "MetricChart"
	GroupWidgetType       WidgetType = "Group"
	MarkdownWidgetType    WidgetType = "Markdown"
)

// WidgetPosition represents the position of a widget relative to its parent
type WidgetPosition struct {
	X *int `json:"x,omitempty"`
	Y *int `json:"y,omitempty"`
	W *int `json:"w,omitempty" jsonschema:"required,description=The width of the widget. The dashboard is divided into 12 columns.For example a sensible value for a graph would be 6"`
	H *int `json:"h,omitempty" jsonschema:"required,description=The height of the widget. Each row is 128px. A sensible value for a graph would be 3."`
}

// Widget is the base interface for all widget types
type Widget struct {
	WidgetType WidgetType      `json:"widgetType" jsonschema:"required,description=The type of the widget. This can be MetricChart / Group / Markdown"`
	Position   *WidgetPosition `json:"position,omitempty" jsonschema:"description=The position of the widget in the dashboard"`
}

// GroupWidget represents a group of widgets
type GroupWidget struct {
	Widget   `json:",inline"`
	Title    *string             `json:"title,omitempty" jsonschema:"description=The title of the group widget if present"`
	Children []MetricChartWidget `json:"children" jsonschema:"description=The children widgets of the group widget. The children are MetricChartWidgets."`
	//Variables []Variable `json:"variables,omitempty"`
}

// MetricChartWidget represents a metric chart widget
type MetricChartWidget struct {
	Widget         `json:",inline"`
	MetricName     string              `json:"metricName" jsonschema:"description=The name of the metric to use in the chart if MetricType is metric. If MetricType is trace, this is not used and can be empty. This value is same as the metricName in the get_metric tool and the possible metricNames can be found in the get_metric_names tool"`
	Filters        map[string][]string `json:"filters,omitempty" jsonschema:"description=The filters to apply to the metric. This is the same as the filters in the get_metric or get_trace_metric tool depending on the MetricType"`
	ExcludeFilters map[string][]string `json:"excludeFilters,omitempty" jsonschema:"description=The exclude filters to apply to the metric. This is the same as the exclude filters in the get_metric or get_trace_metric tool depending on the MetricType"`
	Splits         []string            `json:"splits,omitempty" jsonshcema:"description=Splits will allow you to group/split metrics by an attribute. This is useful if you would like to see the breakdown of a particular metric by an attribute. For example if you want to see the breakdown of the metric by X you would set the splits as ['X']"`
	Aggregation    string              `json:"aggregation" jsonschema:"description=The aggregation to apply to the metrics. This is the same as the aggregation in the get_metric or get_trace_metric tool depending on the MetricType"`
	Title          *string             `json:"title,omitempty" jsonschema:"description=The title of the metric chart widget if present"`
	Type           ChartType           `json:"type" jsonschema:"description=The type of the chart to display. Possible values are line / bar."`
	MetricType     MetricType          `json:"metricType" jsonschema:"description=The type of the metric to use in the chart. Possible values are metric / trace. If metric, the metricName should be used."`
	Functions      []MetricFunction    `json:"functions" jsonschema:"description=The functions to apply to the metric. This is the same as the functions in the get_metric or get_trace_metric tool depending on the MetricType"`
}

// MarkdownWidget represents a markdown content widget
type MarkdownWidget struct {
	Widget  `json:",inline"`
	Content string `json:"content"`
}
type ChartType string

const (
	ChartTypeLine ChartType = "line"
	ChartTypeBar  ChartType = "bar"
)

type MetricType string

const (
	Metric MetricType = "metric" // please excuse the bad naming... this is a metric timeseries type.
	Trace  MetricType = "trace"  // trace timeseries type.

	Logs MetricType = "logs" // log timeseries type.

	KubernetesResource MetricType = "kubernetes_resource" // kubernetes resource timeseries type.
)

type GetLogMetricRequest struct {
	GetLogsRequest
	Splits     []string         `json:"splits" jsonschema:"description=Splits will allow you to group/split metrics by an attribute. This is useful if you would like to see the breakdown of a particular metric by an attribute. For example if you want to see the breakdown of the metric by service.name you would set the splits as ['service.name']"`
	Functions  []MetricFunction `json:"functions" jsonschema:"description=The functions to apply to the log metric. Available functions are monotonicDifference which will calculate the difference between the current and previous value of the metric (negative values will be set to 0) and valueDifference which will calculate the difference between the current and previous value of the metric or MathExpression e.g. a / 60"`
	BucketSize int64            `json:"bucketSize" jsonschema:"description=The size of each datapoint bucket in seconds if not provided metoro will select the best bucket size for the given duration for performance and clarity"`
}

type GetKubernetesResourceRequest struct {
	// Required: Start time of when to get the service summaries in seconds since epoch
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the service summaries in seconds since epoch
	EndTime int64 `json:"endTime"`
	// The filters to apply to the kubernetes summary, so for example, if you want to get kubernetess for a specific service
	// you can pass in a filter like {"service.name": ["microservice_a"]}
	Filters map[string][]string `json:"filters"`
	// ExcludeFilters are filters that should be excluded from the kubernetes summary
	// For example, if you want to get kubernetess for all services except microservice_a you can pass in
	// {"service_name": ["microservice_a"]}
	ExcludeFilters map[string][]string `json:"excludeFilters"`
	// Splts is a list of attributes to split the metrics by, for example, if you want to split the metrics by service
	// you can pass in a list like ["service_name"]
	Splits []string `json:"splits"`
	// The cluster/environments to get the kubernetes metrics for. If empty, all clusters will be included
	Environments []string `json:"environments"`
	// Functions is the list of functions to apply to the metric, in the same order that they appear in this array!!
	Functions []MetricFunction `json:"functions"`
	// LimitResults is a flag to indicate if the results should be limited.
	LimitResults bool `json:"limitResults"`
	// BucketSize is the size of each datapoint bucket in seconds
	BucketSize int64 `json:"bucketSize"`
	// Aggregation is the operation to apply to the metrics, for example, if you want to sum the metrics you can pass in "sum"
	Aggregation Aggregation `json:"aggregation"`
	// JsonPath is a path to pull the json value from the metric
	JsonPath *string `json:"jsonPath"`
}

type GetMultiMetricRequest struct {
	// Required: Start time of when to get the service summaries in seconds
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the service summaries in seconds
	EndTime  int64                 `json:"endTime"`
	Metrics  []SingleMetricRequest `json:"metrics" jsonschema:"required,description=Array of metrics to get the timeseries data for"`
	Formulas []Formula             `json:"formulas" jsonschema:"description=Optional formulas to combine metrics/log metrics/trace metrics. Formula should only consist of formulaIdentifier of the metrics/logs/traces in the metrics array"`
}

type SingleMetricRequest struct {
	Type               string                        `json:"type" jsonschema:"required,enum=metric,enum=trace,enum=logs,enum=kubernetes_resource,description=Type of metric to retrieve"`
	Metric             *GetMetricRequest             `json:"metric,omitempty" jsonschema:"description=Metric request details when type is 'metric'"`
	Trace              *GetTraceMetricRequest        `json:"trace,omitempty" jsonschema:"description=Trace metric request details when type is 'trace'"`
	Logs               *GetLogMetricRequest          `json:"logs,omitempty" jsonschema:"description=Log metric request details when type is 'logs'"`
	KubernetesResource *GetKubernetesResourceRequest `json:"kubernetes,omitempty" jsonschema:"description=Kubernetes resource request details when type is 'kubernetes_resource'"`
	ShouldNotReturn    bool                          `json:"shouldNotReturn" jsonschema:"description=If true result won't be returned (useful for formulas)"`
	FormulaIdentifier  string                        `json:"formulaIdentifier" jsonschema:"description=Identifier to reference this metric in formulas"`
}

// TODO: Add kubernetes resource request type attributes.
type SingleTimeseriesRequest struct {
	Type              MetricType          `json:"type" jsonschema:"required,enum=metric,enum=trace,enum=logs,enum=kubernetes_resource,description=Type of timeseries data to retrieve. YOU MUST SET THIS TO ONE OF THE AVAILABLE TYPES."`
	MetricName        string              `json:"metricName" jsonschema:"description=THIS IS ONLY REQUIRED IF THE type is 'metric'.The name of the metric to use for getting the timeseries data for type 'metric'. If metric name ends with _total metoro already accounts for rate differences when returning the value so you don't need to calculate the rate yourself."`
	Aggregation       Aggregation         `json:"aggregation" jsonschema:"required,enum=sum,enum=count,enum=min,enum=max,enum=avg,enum=p50,enum=p90,enum=p95,enum=p99,description=The aggregation to apply to the timeseries at the datapoint bucket size level. The aggregation will be applied to every datapoint bucket. For example if the bucket size is 1 minute and the aggregation is sum then the sum of all datapoints in a minute will be returned. Do not guess the aggregations. Use the available ones. For traces you can use count p50 p90 p95 p99. for logs its always count. For metrics you can use sum min max avg"`
	JsonPath          *string             `json:"jsonPath" jsonschema:"description=THIS IS ONLY BE SET IF THE type is 'kubernetes_resource' and the aggregate is not count. The json path to use to get the value from the kubernetes resource to plot. for example if this was spec.replicas then the value we return would be aggregate(spec.replicas)"`
	Filters           map[string][]string `json:"filters" jsonschema:"description=Filters to apply to the timeseries. Only the timeseries that match these filters will be returned. You MUST call get_attribute_keys and get_attribute_values tools to get the valid filter keys and values. e.g. {service_name: [/k8s/namespaceX/serviceX]} should return timeseries for serviceX in namespaceX. This is just and example. Do not guess the attribute keys and values."`
	ExcludeFilters    map[string][]string `json:"excludeFilters" jsonschema:"description=Filters to exclude the timeseries data. Timeseries matching the exclude filters will not be returned. You MUST call get_attribute_keys and get_attribute_values tools to get the valid filter keys and values. e.g. {service_name: [/k8s/namespaceX/serviceX]} should exclude timeseries from serviceX in namespaceX. This is just and example. Do not guess the attribute keys and values"`
	Splits            []string            `json:"splits" jsonschema:"description=Array of attribute keys to split/group by the timeseries data by. Splits will allow you to group timeseries data by an attribute. This is useful if you would like to see the breakdown of a particular timeseries by an attribute. Get the attributes that you can pass into as Splits from the get_attribute_keys tool. DO NOT GUESS THE ATTRIBUTES."`
	Regexes           []string            `json:"regexes" jsonschema:"description=This should only be set if the type is 'logs'. Regexes are evaluated against the log message/body. Only the timeseries (logs) data that match these regexes will be returned. Regexes are ANDed together. For example if you want to get log count with message that contains the words 'fish' and 'chips' you would set the regexes as ['fish' 'chips']"`
	ExcludeRegexes    []string            `json:"excludeRegexes" jsonschema:"description=This should only be set if the type is 'logs'. Exclude regexes are evaluated against the log message/body. Log timeseries data that match these regexes will not be returned. Exclude regexes are ORed together. For example if you want to get timeseries data with messages that do not contain the word 'fish' or 'chips' you would set the exclude regexes as ['fish' 'chips']"`
	BucketSize        int64               `json:"bucketSize" jsonschema:"description=The size of each datapoint bucket in seconds if not provided metoro will select the best bucket size for the given duration for performance and clarity"`
	Functions         []MetricFunction    `json:"functions" jsonschema:"description=Array of functions to apply to the timeseries data in the order as it appears in the array. Functions will be applied to the timeseries data after the aggregation. For example if the aggregation is sum and the function is perSecond then the perSecond of the sum will be returned. Do not guess the functions. Use the available ones. For traces you can use rate. For logs you can use count. For metrics you can use rate sum min max avg. For kubernetes resources you can use rate sum min max avg"`
	ShouldNotReturn   bool                `json:"shouldNotReturn" jsonschema:"description=If true result won't be returned (useful for formulas). Only set this to true if you only want to see the combination of timeseries via defining formulas and if you dont want to see the individual timeseries data.'"`
	FormulaIdentifier string              `json:"formulaIdentifier" jsonschema:"description=Identifier to reference this metric in formulas. These must be unique for timeseries that you are requesting the first timeseries must be 'a' the second 'b' and so on. If you are not using formulas you can leave this empty. If you are using formulas then you must set this to a unique identifier for each timeseries. For example if you have 3 timeseries and you want to use them in a formula then you would set the first timeseries to 'a' the second to 'b' and the third to 'c'. You can then use these identifiers in the formulas.'"`
}

type MetricSpecifier struct {
	MetricType     MetricType          `json:"metricType" jsonschema:"required,enum=metric,enum=trace,enum=logs,enum=kubernetes_resource,description=Type of timeseries data to retrieve. YOU MUST SET THIS TO ONE OF THE AVAILABLE TYPES."`
	MetricName     string              `json:"metricName" jsonschema:"description=THIS IS ONLY REQUIRED IF THE type is 'metric'.The name of the metric to use for getting the timeseries data for type 'metric'. If metric name ends with _total metoro already accounts for rate differences when returning the value so you don't need to calculate the rate yourself."`
	Filters        map[string][]string `json:"filters" jsonschema:"description=Filters to apply to the timeseries. Only the timeseries that match these filters will be returned. You MUST call get_attribute_keys and get_attribute_values tools to get the valid filter keys and values. Do not guess the attribute keys and values."`
	ExcludeFilters map[string][]string `json:"excludeFilters" jsonschema:"description=Filters to exclude the timeseries data. Timeseries matching the exclude filters will not be returned. You MUST call get_attribute_keys and get_attribute_values tools to get the valid filter keys and values. Do not guess the attribute keys and values"`
	Regexes        []string            `json:"regexes" jsonschema:"description=This should only be set if the type is 'logs'. Regexes are evaluated against the log message/body. Only the timeseries (logs) data that match these regexes will be returned. Regexes are ANDed together. For example if you want to get log count with message that contains the words 'fish' and 'chips' you would set the regexes as ['fish' 'chips']"`
	ExcludeRegexes []string            `json:"excludeRegexes" jsonschema:"description=This should only be set if the type is 'logs'. Exclude regexes are evaluated against the log message/body. Log timeseries data that match these regexes will not be returned. Exclude regexes are ORed together. For example if you want to get timeseries data with messages that do not contain the word 'fish' or 'chips' you would set the exclude regexes as ['fish' 'chips']"`
	Splits         []string            `json:"splits" jsonschema:"description=Array of attribute keys to split/group by the timeseries data by. Splits will allow you to group timeseries data by an attribute. This is useful if you would like to see the breakdown of a particular timeseries by an attribute. Get the attributes that you can pass into as Splits from the get_attribute_keys tool. DO NOT GUESS THE ATTRIBUTES."`
	Aggregation    Aggregation         `json:"aggregation" jsonschema:"required,enum=sum,enum=count,enum=min,enum=max,enum=avg,enum=p50,enum=p90,enum=p95,enum=p99,description=The aggregation to apply to the timeseries at the datapoint bucket size level. The aggregation will be applied to every datapoint bucket. For example if the bucket size is 1 minute and the aggregation is sum then the sum of all datapoints in a minute will be returned. Do not guess the aggregations. Use the available ones. For traces you can use count p50 p90 p95 p99. for logs its always count. For metrics you can use sum min max avg"`
	BucketSize     int64               `json:"bucketSize" jsonschema:"description=The size of each datapoint bucket in seconds if not provided metoro will select the best bucket size for the given duration for performance and clarity"`
	Functions      []MetricFunction    `json:"functions" jsonschema:"description=Array of functions to apply to the timeseries data in the order as it appears in the array. Functions will be applied to the timeseries data after the aggregation. For example if the aggregation is sum and the function is perSecond then the perSecond of the sum will be returned. Do not guess the functions. Use the available ones. For traces you can use rate. For logs you can use count. For metrics you can use rate sum min max avg. For kubernetes resources you can use rate sum min max avg"`
	//JsonPath        *string             `json:"jsonPath"`
	ShouldNotReturn   bool   `json:"shouldNotReturn" jsonschema:"description=If true result won't be returned (useful for formulas). Only set this to true if you only want to see the combination of timeseries via defining formulas and if you dont want to see the individual timeseries data.'"`
	FormulaIdentifier string `json:"formulaIdentifier" jsonschema:"description=Identifier to reference this metric in formulas. These must be unique for timeseries that you are requesting the first timeseries must be 'a' the second 'b' and so on. If you are not using formulas you can leave this empty. If you are using formulas then you must set this to a unique identifier for each timeseries. For example if you have 3 timeseries and you want to use them in a formula then you would set the first timeseries to 'a' the second to 'b' and the third to 'c'. You can then use these identifiers in the formulas.'"`
}

type Formula struct {
	Formula string `json:"formula" jsonschema:"description=Math expression combining metric results using their formula identifiers"`
}

type GetMetricAttributesRequest struct {
	// Required: The metric name to get the summary for
	MetricName string `json:"metricName"`
	// Required: Start time of when to get the service summaries in seconds since epoch
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the service summaries in seconds since epoch
	EndTime int64 `json:"endTime"`
	// Environments is the environments to get the traces for. If empty, all environments will be included
	Environments []string `json:"environments"`
}

type MultiMetricAttributeKeysRequest struct {
	Type   string                      `json:"type"`
	Metric *GetMetricAttributesRequest `json:"metric,omitempty"`
	// Currently trace and logs and kubernetes resource do not have any request parameters
	// Only metric has request parameters
}

type GetAttributeValuesRequest struct {
	Type       MetricType                    `json:"type"`
	Attribute  string                        `json:"attribute"`
	Limit      *int                          `json:"limit"`
	Metric     *GetMetricAttributesRequest   `json:"metric,omitempty"`
	Trace      *TracesSummaryRequest         `json:"trace,omitempty"`
	Logs       *LogSummaryRequest            `json:"logs,omitempty"`
	Kubernetes *GetKubernetesResourceRequest `json:"kubernetes,omitempty"`
}

type GetAttributeKeysResponse struct {
	// The attribute values
	Attributes []string `json:"attributes"`
}

type GetMetricNamesResponse struct {
	MetricNames []string `json:"metrics"`
}

// Investigation related types
type CreateInvestigationRequest struct {
	Title              string            `json:"title" binding:"required"`
	Summary            string            `json:"summary" binding:"required"`
	RecommendedActions *[]string         `json:"recommendedActions,omitempty"`
	Markdown           string            `json:"markdown" binding:"required"`
	Tags               map[string]string `json:"tags,omitempty"`
	IssueStartTime     *time.Time        `json:"issueStartTime,omitempty"`
	IssueEndTime       *time.Time        `json:"issueEndTime,omitempty"`
	ChatHistoryUUID    *string           `json:"chatHistoryUuid,omitempty"`
	// Optional, these ideally should only set by the AI.
	IsVisible            *bool   `json:"isVisible,omitempty"`
	MetoroApprovalStatus *string `json:"metoroApprovalStatus,omitempty"`
	IssueUUID            *string `json:"issueUuid,omitempty"`
	InProgress           *bool   `json:"inProgress,omitempty"`
	AlertFireUUID        *string `json:"alertFireUuid,omitempty"`
	AlertUUID            *string `json:"alertUuid,omitempty"`
}

type UpdateInvestigationRequest struct {
	Title                   *string            `json:"title,omitempty"`
	Summary                 *string            `json:"summary,omitempty"`
	Markdown                *string            `json:"markdown,omitempty"`
	Tags                    *map[string]string `json:"tags,omitempty"`
	IssueStartTime          *time.Time         `json:"issueStartTime,omitempty"`
	IssueEndTime            *time.Time         `json:"issueEndTime,omitempty"`
	IssueUUID               *string            `json:"issueUuid,omitempty"`
	ParentInvestigationUUID *string            `json:"parentInvestigationUuid,omitempty"`
	IsVisible               *bool              `json:"isVisible,omitempty"`
	MetoroApprovalStatus    *string            `json:"metoroApprovalStatus,omitempty"`
	ChatHistoryUUID         *string            `json:"chatHistoryUuid,omitempty"`
	RecommendedActions      *[]string          `json:"recommendedActions,omitempty"`
	InProgress              *bool              `json:"inProgress,omitempty"`
}

type CreateAIIssueRequest struct {
	Title       string `json:"title"`
	Description string `json:"description"`
	Summary     string `json:"summary"`
}

type UpdateAIIssueRequest struct {
	Title       *string `json:"title,omitempty"`
	Description *string `json:"description,omitempty"`
	Summary     *string `json:"summary,omitempty"`
	Open        *bool   `json:"open,omitempty"`
}

type AIIssue struct {
	UUID             string    `json:"uuid"`
	OrganizationUUID string    `json:"organizationUuid"`
	Title            string    `json:"title"`
	Description      string    `json:"description"`
	Summary          string    `json:"summary"`
	Open             bool      `json:"open"`
	CreatedAt        time.Time `json:"createdAt"`
	UpdatedAt        time.Time `json:"updatedAt"`
}

type ListAIIssuesResponse struct {
	Issues []AIIssue `json:"issues"`
}

type GetAIIssueResponse struct {
	Issue AIIssue `json:"issue"`
}

type AIIssueEvent struct {
	UUID              string     `json:"uuid"`
	IssueUUID         string     `json:"issueUuid"`
	Type              string     `json:"type"`
	CommitSHA         *string    `json:"commitSha,omitempty"`
	VCSLink           *string    `json:"vcsLink,omitempty"`
	MetoroLink        *string    `json:"metoroLink,omitempty"`
	Version           *string    `json:"version,omitempty"`
	Environment       *string    `json:"environment,omitempty"`
	Description       *string    `json:"description,omitempty"`
	InvestigationUUID *string    `json:"investigationUuid,omitempty"`
	OccurrenceTime    *time.Time `json:"occurrenceTime,omitempty"`
	CreatedAt         time.Time  `json:"createdAt"`
}

type ListAIIssueEventsResponse struct {
	Events []AIIssueEvent `json:"events"`
}

type Log struct {
	// The time that the log line was emitted in milliseconds since the epoch
	Time int64 `json:"time"`
	// The severity of the log line
	Severity string `json:"severity"`
	// The log message
	Message string `json:"message"`
	// The attributes of the log line
	LogAttributes map[string]string `json:"logAttributes"`
	// The attributes of the resource that emitted the log line
	ResourceAttributes map[string]string `json:"resourceAttributes"`
	// Service name
	ServiceName string `json:"serviceName"`
	// Environment
	Environment string `json:"environment"`
}

type GetLogsResponse struct {
	// The logs that match the filters
	Logs []Log `json:"logs"`
}

type Link struct {
	// The trace id of the linked trace
	TraceId string `json:"traceId"`
	// The span id of the linked trace
	SpanId string `json:"spanId"`
	// Attributes of the link
	Attributes map[string]string `json:"attributes"`
}
type TraceEl struct {
	// The id of the trace
	TraceId string `json:"traceId"`
	// Status Code of the trace
	StatusCode string `json:"statusCode"`
	// The time that the trace was emitted in milliseconds since the epoch
	Time int64 `json:"time"`
	// The attributes of the trace
	SpanAttributes map[string]string `json:"spanAttributes"`
	// The attributes of the resource that emitted the trace
	ResourceAttributes map[string]string `json:"resourceAttributes"`
	// Service name
	ServiceName string `json:"serviceName"`
	// Display Service name
	DisplayServiceName string `json:"displayServiceName"`
	// Client name
	ClientName string `json:"clientName"`
	// Display Client name
	DisplayClientName string `json:"displayClientName"`
	// Span Id
	SpanId string `json:"spanId"`
	// Span Name
	SpanName string `json:"spanName"`
	// The duration of the trace
	Duration int64 `json:"duration"`
	// Human readable duration, e.g. "1.2s" or "500ms"
	DurationReadable string `json:"durationReadable"`
	// The parent span id
	ParentSpanId string `json:"parentSpanId"`
	// Links
	Links []Link `json:"links"`
}

type GetTracesResponse struct {
	// The traces that match the filters
	Traces []TraceEl `json:"traces"`
}

type MetricSpecifiersRequest struct {
	MetricSpecifiers []MetricSpecifier `json:"metricSpecifiers" binding:"required"`
	Formulas         []Formula         `json:"formulas"`
}

type MetricSpecifierToMetoroQLResponse struct {
	Queries []string `json:"queries"`
}

```
Page 2/2FirstPrevNextLast