#
tokens: 49518/50000 79/98 files (page 1/2)
lines: off (toggle) GitHub
raw markdown copy
This is page 1 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

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
.idea/*
metoro-mcp-server/metoro-mcp-server

```

--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------

```yaml
version: 2

before:
  hooks:
    - go mod tidy

builds:
  - main: ./main.go
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - windows
      - darwin
    goarch:
      - amd64
      - arm64
    mod_timestamp: '{{ .CommitTimestamp }}'
    flags:
      - -trimpath
    ldflags:
      - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }}

archives:
  - format: tar.gz
    name_template: >-
      {{ .ProjectName }}_
      {{- title .Os }}_
      {{- if eq .Arch "amd64" }}x86_64
      {{- else }}{{ .Arch }}{{ end }}
    format_overrides:
      - goos: windows
        format: zip

changelog:
  sort: asc
  filters:
    exclude:
      - '^docs:'
      - '^test:'
      - '^ci:'
      - Merge pull request
      - Merge branch

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
<div align="center">
<img src="./images/Metoro_square.svg" height="300" alt="Metoro MCP Logo">
</div>
<br/>
<div align="center">

![GitHub stars](https://img.shields.io/github/stars/metoro-io/metoro-mcp-server?style=social)
![GitHub forks](https://img.shields.io/github/forks/metoro-io/metoro-mcp-server?style=social)
![GitHub issues](https://img.shields.io/github/issues/metoro-io/metoro-mcp-server)
![GitHub pull requests](https://img.shields.io/github/issues-pr/metoro-io/metoro-mcp-server)
![GitHub license](https://img.shields.io/github/license/metoro-io/metoro-mcp-server)
![GitHub contributors](https://img.shields.io/github/contributors/metoro-io/metoro-mcp-server)
![GitHub last commit](https://img.shields.io/github/last-commit/metoro-io/metoro-mcp-server)
[![GoDoc](https://pkg.go.dev/badge/github.com/metoro-io/metoro-mcp-server.svg)](https://pkg.go.dev/github.com/metoro-io/metoro-mcp-server)
[![Go Report Card](https://goreportcard.com/badge/github.com/metoro-io/metoro-mcp-server)](https://goreportcard.com/report/github.com/metoro-io/metoro-mcp-server)
![Tests](https://github.com/metoro-io/metoro-mcp-server/actions/workflows/go-test.yml/badge.svg)

</div>

# metoro-mcp-server
This repository contains th Metoro MCP (Model Context Protocol) Server. This MCP Server allows you to interact with your Kubernetes cluster via the Claude Desktop App!

## What is MCP (Model Context Protocol)? 
You can read more about the Model Context Protocol here: https://modelcontextprotocol.io

But in a nutshell
> The Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools. Whether you’re building an AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to connect LLMs with the context they need.

## What is Metoro?
[Metoro](https://metoro.io/) is an observability platform designed for microservices running in Kubernetes and uses eBPF based instrumentation to generate deep telemetry without code changes.
The data that is generated by the eBPF agents is sent to Metoro's backend to be stored and in the Metoro frontend using our apis.

This MCP server exposes those APIs to an LLM so you can ask your AI questions about your Kubernetes cluster.

## Demo

https://github.com/user-attachments/assets/b3f21e9a-45b8-4c17-8d8c-cff560d8694f

## How can I use Metoro MCP Server? 
1. Install the [Claude Desktop App](https://claude.ai/download).
2. Make sure you have [Golang](https://golang.org/dl/) installed. `brew install go` for mac or `sudo apt-get install golang` for ubuntu.
3. Clone the repository: `git clone https://github.com/metoro-io/metoro-mcp-server.git`
4. Navigate to the repository directory: `cd metoro-mcp-server`
5. Build the server executable: `go build -o metoro-mcp-server`

### If you already have a Metoro Account:
Copy your auth token from your Metoro account in [Settings](https://us-east.metoro.io/settings) -> Users Settings. 
Create a file in `~/Library/Application Support/Claude/claude_desktop_config.json` with the following contents:
```json
{
  "mcpServers": {
    "metoro-mcp-server": {
      "command": "<your path to Metoro MCP server go executable>/metoro-mcp-server",
      "args": [],
      "env": {
          "METORO_AUTH_TOKEN" : "<your auth token>",
          "METORO_API_URL": "https://us-east.metoro.io"
       }
    }
  }
}
```

### If you don't have a Metoro Account:
No worries, you can still play around using the [Live Demo Cluster](https://demo.us-east.metoro.io/).
The included token is a demo token, publicly available for anyone to use.
   Create a file in `~/Library/Application Support/Claude/claude_desktop_config.json` with the following contents:
```json
{
  "mcpServers": {
    "metoro-mcp-server": {
      "command": "<your path to Metoro MCP server go executable>/metoro-mcp-server",
      "args": [],
      "env": {
          "METORO_AUTH_TOKEN" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjdXN0b21lcklkIjoiOThlZDU1M2QtYzY4ZC00MDRhLWFhZjItNDM2ODllNWJiMGUzIiwiZW1haWwiOiJ0ZXN0QGNocmlzYmF0dGFyYmVlLmNvbSIsImV4cCI6MTgyMTI0NzIzN30.7G6alDpcZh_OThYj293Jce5rjeOBqAhOlANR_Fl5auw",
          "METORO_API_URL": "https://demo.us-east.metoro.io"
       }
    }
  }
}
```

4. Once you are done editing `claude_desktop_config.json` save the file and restart Claude Desktop app.
5. You should now see the Metoro MCP Server in the dropdown list of MCP Servers in the Claude Desktop App. You are ready to start using Metoro MCP Server with Claude Desktop App!

## Built with

This server is built on top of our [Golang MCP SDK](https://github.com/metoro-io/mcp-golang).

```

--------------------------------------------------------------------------------
/.github/workflows/go-test.yml:
--------------------------------------------------------------------------------

```yaml
name: Go Test

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: '>=1.23'
          cache: false

      - name: Run tests
        run: go test -v ./...

```

--------------------------------------------------------------------------------
/resources/services.go:
--------------------------------------------------------------------------------

```go
package resources

import (
	"context"

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

func ServicesResourceHandler() (*mcpgolang.ResourceResponse, error) {
	ctx := context.Background()
	response, err := utils.MakeMetoroAPIRequest("GET", "services", nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, err
	}

	return mcpgolang.NewResourceResponse(
		mcpgolang.NewTextEmbeddedResource("api://services", string(response), "text/plain")), nil
}

```

--------------------------------------------------------------------------------
/resources/namespaces.go:
--------------------------------------------------------------------------------

```go
package resources

import (
	"context"

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

func NamespacesResourceHandler() (*mcpgolang.ResourceResponse, error) {
	ctx := context.Background()
	response, err := utils.MakeMetoroAPIRequest("GET", "namespaces", nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, err
	}

	return mcpgolang.NewResourceResponse(
		mcpgolang.NewTextEmbeddedResource("api://namespaces", string(response), "text/plain")), nil
}

```

--------------------------------------------------------------------------------
/resources/environments.go:
--------------------------------------------------------------------------------

```go
package resources

import (
	"context"

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

func EnvironmentResourceHandler() (*mcpgolang.ResourceResponse, error) {
	ctx := context.Background()
	response, err := utils.MakeMetoroAPIRequest("GET", "environments", nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, err
	}

	return mcpgolang.NewResourceResponse(
		mcpgolang.NewTextEmbeddedResource("api://environments", string(response), "text/plain")), nil

}

```

--------------------------------------------------------------------------------
/resources/log_attributes.go:
--------------------------------------------------------------------------------

```go
package resources

import (
	"context"

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

func LogAttributesResourceHandler() (*mcpgolang.ResourceResponse, error) {
	ctx := context.Background()
	resp, err := utils.MakeMetoroAPIRequest("GET", "logsSummaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, err
	}

	return mcpgolang.NewResourceResponse(
		mcpgolang.NewTextEmbeddedResource("api://logAttributes", string(resp), "text/plain")), nil
}

```

--------------------------------------------------------------------------------
/resources/trace_attributes.go:
--------------------------------------------------------------------------------

```go
package resources

import (
	"context"

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

func TraceAttributesResourceHandler() (*mcpgolang.ResourceResponse, error) {
	ctx := context.Background()
	resp, err := utils.MakeMetoroAPIRequest("GET", "tracesSummaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, err
	}

	return mcpgolang.NewResourceResponse(
		mcpgolang.NewTextEmbeddedResource("api://traceAttributes", string(resp), "text/plain")), nil
}

```

--------------------------------------------------------------------------------
/resources/k8s_events_attributes.go:
--------------------------------------------------------------------------------

```go
package resources

import (
	"context"

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

func K8sEventsAttributesResourceHandler() (*mcpgolang.ResourceResponse, error) {
	ctx := context.Background()
	resp, err := utils.MakeMetoroAPIRequest("GET", "k8s/events/summaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, err
	}

	return mcpgolang.NewResourceResponse(
		mcpgolang.NewTextEmbeddedResource("api://k8sEventAttributes", string(resp), "text/plain")), nil
}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type GetTraceAttributesHandlerArgs struct{}

func GetTraceAttributesHandler(ctx context.Context, arguments GetTraceAttributesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	resp, err := utils.MakeMetoroAPIRequest("GET", "tracesSummaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, err
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type GetK8sEventsAttributesHandlerArgs struct{}

func GetK8sEventsAttributesHandler(ctx context.Context, arguments GetK8sEventsAttributesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	resp, err := utils.MakeMetoroAPIRequest("GET", "k8s/events/summaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, err
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type GetLogAttributesHandlerArgs struct{}

func GetLogAttributesHandler(ctx context.Context, arguments GetLogAttributesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	resp, err := utils.MakeMetoroAPIRequest("GET", "logsSummaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, fmt.Errorf("error making Metoro call: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

```

--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------

```yaml
name: Release with GoReleaser

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: '>=1.23'
          cache: false
          
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6
        with:
          distribution: goreleaser
          version: latest
          args: release --clean
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type GetAlertHandlerArgs struct{}

func GetAlertsHandler(ctx context.Context, arguments GetAlertHandlerArgs) (*mcpgolang.ToolResponse, error) {
	body, err := getAlertsMetoroCall(ctx)
	if err != nil {
		return nil, fmt.Errorf("error getting alerts: %v", err)
	}
	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

func getAlertsMetoroCall(ctx context.Context) ([]byte, error) {
	return utils.MakeMetoroAPIRequest("GET", "searchAlerts", nil, utils.GetAPIRequirementsFromRequest(ctx))
}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type GetServicesHandlerArgs struct{}

func GetServicesHandler(ctx context.Context, arguments GetServicesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	body, err := getServicesMetoroCall(ctx)
	if err != nil {
		return nil, fmt.Errorf("error getting services: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

func getServicesMetoroCall(ctx context.Context) ([]byte, error) {
	return utils.MakeMetoroAPIRequest("GET", "services", nil, utils.GetAPIRequirementsFromRequest(ctx))
}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type GetNamespacesHandlerArgs struct{}

func GetNamespacesHandler(ctx context.Context, arguments GetNamespacesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	body, err := getNamespacesMetoroCall(ctx)
	if err != nil {
		return nil, fmt.Errorf("error getting namespaces: %v", err)
	}
	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

func getNamespacesMetoroCall(ctx context.Context) ([]byte, error) {
	return utils.MakeMetoroAPIRequest("GET", "namespaces", nil, utils.GetAPIRequirementsFromRequest(ctx))
}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type GetEnvironmentHandlerArgs struct{}

func GetEnvironmentsHandler(ctx context.Context, arguments GetEnvironmentHandlerArgs) (*mcpgolang.ToolResponse, error) {
	body, err := getEnvironmentsMetoroCall(ctx)
	if err != nil {
		return nil, fmt.Errorf("error getting environments: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

func getEnvironmentsMetoroCall(ctx context.Context) ([]byte, error) {
	return utils.MakeMetoroAPIRequest("GET", "environments", nil, utils.GetAPIRequirementsFromRequest(ctx))
}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type ListAIIssueHandlerArgs struct {
	OpenOnly *bool `json:"openOnly,omitempty" jsonschema:"description=Set to true to list only open issues (default true), false to list all issues"`
}

func ListAIIssuesHandler(ctx context.Context, arguments ListAIIssueHandlerArgs) (*mcpgolang.ToolResponse, error) {
	openOnly := true
	if arguments.OpenOnly != nil {
		openOnly = *arguments.OpenOnly
	}

	endpoint := "aiIssues"
	if openOnly {
		endpoint += "?openOnly=true"
	}

	responseBody, err := utils.MakeMetoroAPIRequest("GET", endpoint, nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, fmt.Errorf("failed to list AI issues: %w", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type GetMetricMetadataHandlerArgs struct {
	Name string `json:"name" jsonschema:"required,description=The name of the metric to get metadata for"`
}

func GetMetricMetadata(ctx context.Context, arguments GetMetricMetadataHandlerArgs) (*mcpgolang.ToolResponse, error) {
	response, err := getMetricMetadataMetoroCall(ctx, arguments.Name)
	if err != nil {
		return nil, fmt.Errorf("error calling Metoro get metric metadata api: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(response)))), nil
}

func getMetricMetadataMetoroCall(ctx context.Context, metricName string) ([]byte, error) {
	return utils.MakeMetoroAPIRequest("GET", "metric/metadata?name="+metricName, nil, utils.GetAPIRequirementsFromRequest(ctx))
}

```

--------------------------------------------------------------------------------
/resources/metrics.go:
--------------------------------------------------------------------------------

```go
package resources

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

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

func MetricsResourceHandler() (*mcpgolang.ResourceResponse, error) {
	now := time.Now()
	twoHoursAgo := now.Add(-2 * time.Hour)
	request := model.FuzzyMetricsRequest{
		StartTime:        twoHoursAgo.Unix(),
		EndTime:          now.Unix(),
		MetricFuzzyMatch: "",         // This will return all the metric names.
		Environments:     []string{}, // All environments
	}
	jsonData, err := json.Marshal(request)
	if err != nil {
		return nil, err
	}
	ctx := context.Background()
	resp, err := utils.MakeMetoroAPIRequest("POST", "fuzzyMetricsNames", bytes.NewBuffer(jsonData), utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, err
	}
	return mcpgolang.NewResourceResponse(
		mcpgolang.NewTextEmbeddedResource("api://metrics", string(resp), "text/plain")), nil
}

```

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

```go
package tools

import (
	"context"
	"fmt"
	"time"

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

type GetNodeInfoHandlerArgs struct {
	NodeName string `json:"nodeName" jsonschema:"required,description=The name of the node to get the YAML/information for"`
}

func GetNodeInfoHandler(ctx context.Context, arguments GetNodeInfoHandlerArgs) (*mcpgolang.ToolResponse, error) {
	now := time.Now()
	fiveMinsAgo := now.Add(-10 * time.Minute)

	body, err := getNodeInfoMetoroCall(ctx, arguments.NodeName, fiveMinsAgo.Unix())
	if err != nil {
		return nil, fmt.Errorf("error getting node info: %v", err)
	}
	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

func getNodeInfoMetoroCall(ctx context.Context, nodeName string, startTime int64) ([]byte, error) {
	return utils.MakeMetoroAPIRequest("GET", fmt.Sprintf("infrastructure/node?nodeName=%s&startTime=%d", nodeName, startTime), nil, utils.GetAPIRequirementsFromRequest(ctx))
}

```

--------------------------------------------------------------------------------
/resources/nodes.go:
--------------------------------------------------------------------------------

```go
package resources

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

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

func NodesResourceHandler() (*mcpgolang.ResourceResponse, error) {
	now := time.Now()
	tenMinsAgo := now.Add(-10 * time.Minute)
	request := model.GetAllNodesRequest{
		StartTime:      tenMinsAgo.Unix(),
		EndTime:        now.Unix(),
		Filters:        map[string][]string{},
		ExcludeFilters: map[string][]string{},
		Splits:         []string{},
		Environments:   []string{},
	}
	jsonRequest, err := json.Marshal(request)
	if err != nil {
		return nil, err
	}
	ctx := context.Background()
	response, err := utils.MakeMetoroAPIRequest("POST", "infrastructure/nodes", bytes.NewBuffer(jsonRequest), utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, err
	}
	return mcpgolang.NewResourceResponse(
		mcpgolang.NewTextEmbeddedResource("api://nodes", string(response), "text/plain")), nil
}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type GetNodeAttributesHandlerArgs struct {
	TimeConfig utils.TimeConfig `json:"timeConfig" jsonschema:"required,description=The time range to get the node attributes for"`
}

func GetNodeAttributesHandler(ctx context.Context, arguments GetNodeAttributesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.MetricAttributesRequest{
		StartTime:        startTime,
		EndTime:          endTime,
		MetricName:       "node_info",
		FilterAttributes: map[string][]string{},
	}
	response, err := getMetricAttributesMetoroCall(ctx, request)
	if err != nil {
		return nil, fmt.Errorf("error calling Metoro API: %v", err)
	}
	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(response)))), nil
}

```

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

```go
package tools

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

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

type GetAIIssueHandlerArgs struct {
	IssueUUID string `json:"issueUuid" jsonschema:"required,description=UUID of the AI issue to retrieve"`
}

func GetAIIssueHandler(ctx context.Context, arguments GetAIIssueHandlerArgs) (*mcpgolang.ToolResponse, error) {
	endpoint := fmt.Sprintf("aiIssue?uuid=%s", arguments.IssueUUID)
	responseBody, err := utils.MakeMetoroAPIRequest("GET", endpoint, nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, fmt.Errorf("failed to fetch AI issue: %w", err)
	}

	var issueResponse model.GetAIIssueResponse
	if err := json.Unmarshal(responseBody, &issueResponse); err != nil {
		return nil, fmt.Errorf("failed to parse AI issue response: %w", err)
	}

	serialized, err := json.Marshal(issueResponse.Issue)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal AI issue: %w", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(serialized))), nil
}

```

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

```go
package tools

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

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

type ListAIIssueEventsHandlerArgs struct {
	IssueUUID string `json:"issueUuid" jsonschema:"required,description=UUID of the AI issue whose events should be listed"`
}

func ListAIIssueEventsHandler(ctx context.Context, arguments ListAIIssueEventsHandlerArgs) (*mcpgolang.ToolResponse, error) {
	endpoint := fmt.Sprintf("aiIssue/events?issueUuid=%s", arguments.IssueUUID)
	responseBody, err := utils.MakeMetoroAPIRequest("GET", endpoint, nil, utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, fmt.Errorf("failed to list AI issue events: %w", err)
	}

	var resp model.ListAIIssueEventsResponse
	if err := json.Unmarshal(responseBody, &resp); err != nil {
		return nil, fmt.Errorf("failed to parse AI issue events response: %w", err)
	}

	serialized, err := json.Marshal(resp.Events)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal AI issue events: %w", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(serialized))), nil
}

```

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

```go
package tools

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

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

type CreateAIIssueHandlerArgs struct {
	Title       string `json:"title" jsonschema:"required,description=Title of the AI issue"`
	Description string `json:"description" jsonschema:"required,description=Detailed description of the AI issue"`
	Summary     string `json:"summary" jsonschema:"required,description=One sentence summary of the AI issue"`
}

func CreateAIIssueHandler(ctx context.Context, arguments CreateAIIssueHandlerArgs) (*mcpgolang.ToolResponse, error) {
	request := model.CreateAIIssueRequest{
		Title:       arguments.Title,
		Description: arguments.Description,
		Summary:     arguments.Summary,
	}

	requestBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	responseBody, err := utils.MakeMetoroAPIRequest("POST", "aiIssue", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, fmt.Errorf("failed to create AI issue: %w", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
}

```

--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------

```go
package main

import (
	"fmt"
	"os"

	mcpgolang "github.com/metoro-io/mcp-golang"
	"github.com/metoro-io/mcp-golang/transport/stdio"
	"github.com/metoro-io/metoro-mcp-server/resources"
	"github.com/metoro-io/metoro-mcp-server/tools"
	"github.com/metoro-io/metoro-mcp-server/utils"
)

func main() {
	// Check if the appropriate environment variables are set
	if err := checkEnvVars(); err != nil {
		panic(err)
	}

	done := make(chan struct{})

	mcpServer := mcpgolang.NewServer(stdio.NewStdioServerTransport())

	// Add tools
	for _, tool := range tools.MetoroToolsList {
		err := mcpServer.RegisterTool(tool.Name, tool.Description, tool.Handler)
		if err != nil {
			panic(err)
		}
	}

	// Add resources
	for _, resource := range resources.MetoroResourcesList {
		err := mcpServer.RegisterResource(
			resource.Path,
			resource.Name,
			resource.Description,
			resource.ContentType,
			resource.Handler)
		if err != nil {
			panic(err)
		}
	}

	err := mcpServer.Serve()
	if err != nil {
		panic(err)
	}

	<-done
}

func checkEnvVars() error {
	if os.Getenv(utils.METORO_API_URL_ENV_VAR) == "" {
		return fmt.Errorf("%s environment variable not set", utils.METORO_API_URL_ENV_VAR)
	}
	if os.Getenv(utils.METORO_AUTH_TOKEN_ENV_VAR) == "" {
		return fmt.Errorf("%s environment variable not set", utils.METORO_AUTH_TOKEN_ENV_VAR)
	}
	return nil
}

```

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

```go
package tools

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

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

type GetMetricNamesHandlerArgs struct {
	Environments []string `json:"environments" jsonschema:"description=Environments to get metrics names from. If empty all environments will be used."`
}

func GetMetricNamesHandler(ctx context.Context, arguments GetMetricNamesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	now := time.Now()
	hourAgo := now.Add(-1 * time.Hour)
	request := model.FuzzyMetricsRequest{
		StartTime:        hourAgo.Unix(),
		EndTime:          now.Unix(),
		MetricFuzzyMatch: "", // This will return all the metric names.
		Environments:     arguments.Environments,
	}
	response, err := getMetricNamesMetoroCall(ctx, request)
	if err != nil {
		return nil, fmt.Errorf("error calling Metoro API: %v", err)
	}
	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(response)))), nil
}

func getMetricNamesMetoroCall(ctx context.Context, request model.FuzzyMetricsRequest) ([]byte, error) {
	jsonData, err := json.Marshal(request)
	if err != nil {
		return nil, err
	}
	return utils.MakeMetoroAPIRequest("POST", "fuzzyMetricsNames", bytes.NewBuffer(jsonData), utils.GetAPIRequirementsFromRequest(ctx))
}

```

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

```go
package tools

import (
	"context"
	"fmt"

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

type GetAlertFiresHandlerArgs struct {
	TimeConfig utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time period to get alert fires for. e.g. if you want to get alert fires 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"`
	AlertId    string           `json:"alert_id" jsonschema:"required,description=The ID of the alert to get the alert fires for"`
}

func GetAlertFiresHandler(ctx context.Context, arguments GetAlertFiresHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	body, err := getAlertFiresMetoroCall(ctx, arguments.AlertId, startTime, endTime)
	if err != nil {
		return nil, fmt.Errorf("error getting alert fires: %v", err)
	}
	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

func getAlertFiresMetoroCall(ctx context.Context, alertId string, startTime, endTime int64) ([]byte, error) {
	return utils.MakeMetoroAPIRequest("GET", fmt.Sprintf("alertFires?alertId=%s&startTime=%d&endTime=%d", alertId, startTime, endTime), nil, utils.GetAPIRequirementsFromRequest(ctx))
}

```

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

```go
package tools

import (
	"context"
	"fmt"
	"time"

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

type UnixToRFC3339HandlerArgs struct {
	UnixTimestamp int64 `json:"unix_timestamp" jsonschema:"required,description=Unix timestamp in seconds or milliseconds"`
}

func UnixToRFC3339Handler(ctx context.Context, arguments UnixToRFC3339HandlerArgs) (*mcpgolang.ToolResponse, error) {
	// Determine if the timestamp is in seconds or milliseconds
	// Unix timestamps in seconds are typically 10 digits (until year 2286)
	// Unix timestamps in milliseconds are typically 13 digits
	var t time.Time

	// Check if it's likely milliseconds (more than 10 digits or would result in a date far in the future)
	if arguments.UnixTimestamp > 9999999999 {
		// Treat as milliseconds
		t = time.Unix(0, arguments.UnixTimestamp*int64(time.Millisecond))
	} else {
		// Treat as seconds
		t = time.Unix(arguments.UnixTimestamp, 0)
	}

	// Convert to RFC3339 format
	rfc3339String := t.UTC().Format(time.RFC3339)

	// Create a response with both interpretations if the timestamp could be ambiguous
	var response string
	if arguments.UnixTimestamp <= 9999999999 && arguments.UnixTimestamp >= 1000000000 {
		// Could be either seconds or milliseconds, show both
		tAsMillis := time.Unix(0, arguments.UnixTimestamp*int64(time.Millisecond))
		response = fmt.Sprintf("Interpreted as seconds: %s\nInterpreted as milliseconds: %s",
			rfc3339String,
			tAsMillis.UTC().Format(time.RFC3339))
	} else {
		response = rfc3339String
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(response)), nil
}

```

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

```go
package tools

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

	"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 CreateDashboardHandlerArgs struct {
	DashboardName string            `json:"dashboard_name" jsonschema:"required,description=The name of the dashboard to create"`
	GroupWidget   model.GroupWidget `json:"group_widget" jsonschema:"required,description=The group widget this dashboard will have. This is the top level widget of the dashboard that will contain all other widgets. A widget can be either a group widget or a MetricChartWidget"`
}

func CreateDashboardHandler(ctx context.Context, arguments CreateDashboardHandlerArgs) (*mcpgolang.ToolResponse, error) {
	dashboardJson, err := json.Marshal(arguments.GroupWidget)
	if err != nil {
		return nil, fmt.Errorf("error marshaling dashboard properties: %v", err)
	}

	newDashboardRequest := model.SetDashboardRequest{
		Name:             arguments.DashboardName,
		Id:               uuid.NewString(),
		DashboardJson:    string(dashboardJson),
		DefaultTimeRange: "1h",
	}

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

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

```

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

```go
package tools

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

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

type GetK8sServiceInformationHandlerArgs struct {
	TimeConfig   utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time to get state of the YAML file. e.g. if you want to see the state of the service 5 minutes ago 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"`
	ServiceName  string           `json:"serviceName" jsonschema:"required,description=The name of the service to get YAML file for."`
	Environments []string         `json:"environments" jsonschema:"description=The environments to get service YAML for. If empty all environments will be used."`
}

func GetK8sServiceInformationHandler(ctx context.Context, arguments GetK8sServiceInformationHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetPodsRequest{
		StartTime:    startTime,
		EndTime:      endTime,
		ServiceName:  arguments.ServiceName,
		Environments: arguments.Environments,
	}
	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

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

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

```

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

```go
package tools

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

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

type UpdateAIIssueHandlerArgs struct {
	IssueUUID   string  `json:"issueUuid" jsonschema:"required,description=UUID of the AI issue to update"`
	Title       *string `json:"title,omitempty" jsonschema:"description=Optional new title for the AI issue"`
	Description *string `json:"description,omitempty" jsonschema:"description=Optional new description for the AI issue"`
	Summary     *string `json:"summary,omitempty" jsonschema:"description=Optional new summary for the AI issue"`
	Open        *bool   `json:"open,omitempty" jsonschema:"description=Optional flag to set whether the AI issue is open (true) or resolved (false)"`
}

func UpdateAIIssueHandler(ctx context.Context, arguments UpdateAIIssueHandlerArgs) (*mcpgolang.ToolResponse, error) {
	if arguments.Title == nil && arguments.Description == nil && arguments.Summary == nil && arguments.Open == nil {
		return nil, fmt.Errorf("at least one of title, description, summary, or open must be provided to update an AI issue")
	}

	request := model.UpdateAIIssueRequest{
		Title:       arguments.Title,
		Description: arguments.Description,
		Summary:     arguments.Summary,
		Open:        arguments.Open,
	}

	requestBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	endpoint := fmt.Sprintf("aiIssue?uuid=%s", arguments.IssueUUID)
	responseBody, err := utils.MakeMetoroAPIRequest("PUT", endpoint, bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, fmt.Errorf("failed to update AI issue: %w", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
}

```

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

```go
package tools

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

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

type GetProfileHandlerArgs struct {
	TimeConfig     utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time period to get the profiles data. e.g. if you want to get profiles 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"`
	ServiceName    string           `json:"serviceName" jsonschema:"required,description=The name of the service to get profiles for"`
	ContainerNames []string         `json:"containerNames" jsonschema:"description=The container names to get profiles for"`
}

func GetProfilesHandler(ctx context.Context, arguments GetProfileHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetProfileRequest{
		StartTime:      startTime,
		EndTime:        endTime,
		ServiceName:    arguments.ServiceName,
		ContainerNames: arguments.ContainerNames,
	}

	body, err := getProfilesMetoroCall(ctx, request)
	if err != nil {
		return nil, fmt.Errorf("error getting profiles: %v", err)
	}

	if len(body) > 200000 {
		return nil, fmt.Errorf("response too large, please refine your query to get a smaller response")
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

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

```

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

```go
package tools

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

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

type ListInvestigationsHandlerArgs struct {
	Limit           int               `json:"limit,omitempty" jsonschema:"description=Maximum number of investigations to return (default 20 max 100)"`
	Offset          int               `json:"offset,omitempty" jsonschema:"description=Number of investigations to skip for pagination"`
	Tags            map[string]string `json:"tags,omitempty" jsonschema:"description=Filter investigations by tags"`
	IncludeResolved bool              `json:"includeResolved,omitempty" jsonschema:"description=Include resolved investigations in the results"`
}

func ListInvestigationsHandler(ctx context.Context, arguments ListInvestigationsHandlerArgs) (*mcpgolang.ToolResponse, error) {
	// Create the request body
	request := struct {
		Limit             int               `json:"limit,omitempty"`
		Offset            int               `json:"offset,omitempty"`
		Tags              map[string]string `json:"tags,omitempty"`
		IncludeResolved   bool              `json:"includeResolved,omitempty"`
		ExcludeInProgress bool              `json:"excludeInProgress,omitempty"`
	}{
		Limit:             arguments.Limit,
		Offset:            arguments.Offset,
		Tags:              arguments.Tags,
		IncludeResolved:   arguments.IncludeResolved,
		ExcludeInProgress: true, // Always exclude in-progress investigations as the AI only wants to see the past investigations.
	}

	requestBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	// Make the API request
	responseBody, err := utils.MakeMetoroAPIRequest("POST", "investigations/list", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, fmt.Errorf("failed to list investigations: %w", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
}

```

--------------------------------------------------------------------------------
/utils/request_utils.go:
--------------------------------------------------------------------------------

```go
package utils

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
)

const METORO_API_URL_ENV_VAR = "METORO_API_URL"
const METORO_AUTH_TOKEN_ENV_VAR = "METORO_AUTH_TOKEN"

type APIRequirements struct {
	authHeader string
	metoroUrl  string
}

func GetAPIRequirementsFromRequest(ctx context.Context) *APIRequirements {
	c := ctx.Value("ginContext")
	if c == nil {
		return nil
	}
	ginContext, ok := c.(*gin.Context)
	if !ok {
		return nil
	}

	if ginContext.Request.Header.Get("Authorization") != "" {
		return &APIRequirements{
			authHeader: ginContext.Request.Header.Get("Authorization"),
			metoroUrl:  "http://localhost:8080",
		}
	}
	return nil
}

// makeMetoroAPIRequest makes an HTTP request to the Metoro API with the given method, endpoint, and body.
// It handles authentication and common error cases.
func MakeMetoroAPIRequest(method, endpoint string, body io.Reader, apiRequirements *APIRequirements) ([]byte, error) {
	// Create a new HTTP client
	client := &http.Client{}
	if apiRequirements == nil {
		apiRequirements = &APIRequirements{
			authHeader: "Bearer " + os.Getenv(METORO_AUTH_TOKEN_ENV_VAR),
			metoroUrl:  os.Getenv(METORO_API_URL_ENV_VAR),
		}
	}

	// Create a new request
	req, err := http.NewRequest(method, fmt.Sprintf("%s/api/v1/%s", apiRequirements.metoroUrl, endpoint), body)
	if err != nil {
		return nil, fmt.Errorf("error creating request: %v", err)
	}

	// Add the Authorization header
	req.Header.Add("Authorization", apiRequirements.authHeader)

	// Send the request
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("error sending request: %v", err)
	}
	defer resp.Body.Close()

	// Read the response body
	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("error reading response body: %v", err)
	}

	// Check the response status code
	if resp.StatusCode >= 300 {
		return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(responseBody))
	}

	return responseBody, nil
}

```

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

```go
package tools

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

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

type GetServiceSummariesHandlerArgs struct {
	TimeConfig   utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time period to get service summaries for. e.g. if you want to get summaries for the last 5 minutes you would set time_period=5 and time_window=Minutes. Try to use a time period 1 hour or less. You can also set an absoulute time range by setting start_time and end_time"`
	Namespaces   string           `json:"namespace" jsonschema:"description=The namespace to get service summaries for. If empty all namespaces will be used."`
	Environments []string         `json:"environments" jsonschema:"description=The environments to get service summaries for. If empty all environments will be used."`
}

func GetServiceSummariesHandler(ctx context.Context, arguments GetServiceSummariesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetServiceSummariesRequest{
		StartTime:    startTime,
		EndTime:      endTime,
		Namespace:    arguments.Namespaces,
		Environments: arguments.Environments,
	}

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

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

```

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

```go
package tools

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

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

type GetMetricAttributesHandlerArgs struct {
	TimeConfig       utils.TimeConfig    `json:"timeConfig" jsonschema:"required,description=The time period to get the possible values of metric attributes for. e.g. if you want to get the possible values 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"`
	MetricName       string              `json:"metricName" jsonschema:"required,description=The name of the metric to get the possible attribute keys and values."`
	FilterAttributes map[string][]string `json:"filterAttributes" jsonschema:"description=The attributes to filter the metric attributes by before getting the possible values. For example if you want to get the possible keys and values where the environment is X you would set the filterAttributes as {environment: [X]}"`
}

func GetMetricAttributesHandler(ctx context.Context, arguments GetMetricAttributesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.MetricAttributesRequest{
		StartTime:        startTime,
		EndTime:          endTime,
		MetricName:       arguments.MetricName,
		FilterAttributes: arguments.FilterAttributes,
	}
	response, err := getMetricAttributesMetoroCall(ctx, request)
	if err != nil {
		return nil, fmt.Errorf("error calling Metoro API: %v", err)
	}
	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(response)))), nil
}

func getMetricAttributesMetoroCall(ctx context.Context, request model.MetricAttributesRequest) ([]byte, error) {
	jsonData, err := json.Marshal(request)
	if err != nil {
		return nil, err
	}
	return utils.MakeMetoroAPIRequest("POST", "metricAttributes", bytes.NewBuffer(jsonData), utils.GetAPIRequirementsFromRequest(ctx))
}

```

--------------------------------------------------------------------------------
/model/model_alert_type.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"
	"fmt"
)

// AlertType Type of alert
type AlertType string

// List of AlertType
const (
	TIMESERIES AlertType = "timeseries"
)

// All allowed values of AlertType enum
var AllowedAlertTypeEnumValues = []AlertType{
	"timeseries",
}

func (v *AlertType) UnmarshalJSON(src []byte) error {
	var value string
	err := json.Unmarshal(src, &value)
	if err != nil {
		return err
	}
	enumTypeValue := AlertType(value)
	for _, existing := range AllowedAlertTypeEnumValues {
		if existing == enumTypeValue {
			*v = enumTypeValue
			return nil
		}
	}

	return fmt.Errorf("%+v is not a valid AlertType", value)
}

// NewAlertTypeFromValue returns a pointer to a valid AlertType
// for the value passed as argument, or an error if the value passed is not allowed by the enum
func NewAlertTypeFromValue(v string) (*AlertType, error) {
	ev := AlertType(v)
	if ev.IsValid() {
		return &ev, nil
	} else {
		return nil, fmt.Errorf("invalid value '%v' for AlertType: valid values are %v", v, AllowedAlertTypeEnumValues)
	}
}

// IsValid return true if the value is valid for the enum, false otherwise
func (v AlertType) IsValid() bool {
	for _, existing := range AllowedAlertTypeEnumValues {
		if existing == v {
			return true
		}
	}
	return false
}

// Ptr returns reference to AlertType value
func (v AlertType) Ptr() *AlertType {
	return &v
}

type NullableAlertType struct {
	value *AlertType
	isSet bool
}

func (v NullableAlertType) Get() *AlertType {
	return v.value
}

func (v *NullableAlertType) Set(val *AlertType) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableAlertType(val *AlertType) *NullableAlertType {
	return &NullableAlertType{value: val, isSet: true}
}

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

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

```

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

```go
package tools

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

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

type GetResourcesByIpHandlerArgs struct {
	Ip          string           `json:"ip" jsonschema:"required,description=IP address to search for"`
	TimeConfig  utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time period to get the resources for. e.g. if you want the get the resources for the last 5 minutes you would set time_period=5 and time_window=Minutes. You can also set an absolute time range by setting start_time and end_time"`
	Environment string           `json:"environment" jsonschema:"required,description=Environment to filter the resources by"`
}

type GetResourcesByIpRequest struct {
	Ip          string `json:"ip"`
	StartTime   int64  `json:"startTime"`
	EndTime     int64  `json:"endTime"`
	Environment string `json:"environment"`
}

type GetResourcesByIpResponse struct {
	Resources []ResourcesByIpData `json:"resources"`
}

type ResourcesByIpData struct {
	Name        string `json:"name"`
	Namespace   string `json:"namespace"`
	Ip          string `json:"ip"`
	NodeName    string `json:"nodeName"`
	Status      string `json:"status"`
	Environment string `json:"environment"`
}

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

	request := GetResourcesByIpRequest{
		Ip:          arguments.Ip,
		StartTime:   startTime,
		EndTime:     endTime,
		Environment: arguments.Environment,
	}

	resp, err := getPodByIpMetoroCall(ctx, request)
	if err != nil {
		return nil, fmt.Errorf("error getting pod by IP: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

func getPodByIpMetoroCall(ctx context.Context, request GetResourcesByIpRequest) ([]byte, error) {
	requestBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling pod by IP request: %v", err)
	}
	return utils.MakeMetoroAPIRequest("POST", "k8s/resources/byIp", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
}

```

--------------------------------------------------------------------------------
/model/model_condition_type.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"
	"fmt"
)

// ConditionType Type of alert condition
type ConditionType string

// List of ConditionType
const (
	STATIC ConditionType = "static"
)

// All allowed values of ConditionType enum
var AllowedConditionTypeEnumValues = []ConditionType{
	"static",
}

func (v *ConditionType) UnmarshalJSON(src []byte) error {
	var value string
	err := json.Unmarshal(src, &value)
	if err != nil {
		return err
	}
	enumTypeValue := ConditionType(value)
	for _, existing := range AllowedConditionTypeEnumValues {
		if existing == enumTypeValue {
			*v = enumTypeValue
			return nil
		}
	}

	return fmt.Errorf("%+v is not a valid ConditionType", value)
}

// NewConditionTypeFromValue returns a pointer to a valid ConditionType
// for the value passed as argument, or an error if the value passed is not allowed by the enum
func NewConditionTypeFromValue(v string) (*ConditionType, error) {
	ev := ConditionType(v)
	if ev.IsValid() {
		return &ev, nil
	} else {
		return nil, fmt.Errorf("invalid value '%v' for ConditionType: valid values are %v", v, AllowedConditionTypeEnumValues)
	}
}

// IsValid return true if the value is valid for the enum, false otherwise
func (v ConditionType) IsValid() bool {
	for _, existing := range AllowedConditionTypeEnumValues {
		if existing == v {
			return true
		}
	}
	return false
}

// Ptr returns reference to ConditionType value
func (v ConditionType) Ptr() *ConditionType {
	return &v
}

type NullableConditionType struct {
	value *ConditionType
	isSet bool
}

func (v NullableConditionType) Get() *ConditionType {
	return v.value
}

func (v *NullableConditionType) Set(val *ConditionType) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableConditionType(val *ConditionType) *NullableConditionType {
	return &NullableConditionType{value: val, isSet: true}
}

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

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

```

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

```go
package tools

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

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

type GetPodsHandlerArgs struct {
	TimeConfig   utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time period to get pods for. e.g. if you want to get pods 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"`
	ServiceName  string           `json:"serviceName" jsonschema:"description=The name of the service to get pods for. One of serviceName or nodeName is required"`
	NodeName     string           `json:"nodeName" jsonschema:"description=The name of the node to get pods for. One of serviceName or nodeName is required"`
	Environments []string         `json:"environments" jsonschema:"description=The environments to get pods for. If empty, all environments will be used."`
}

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

	// One of serviceName or nodeName is required.
	if arguments.ServiceName == "" && arguments.NodeName == "" {
		return nil, fmt.Errorf("one of serviceName or nodeName is required")
	}

	request := model.GetPodsRequest{
		StartTime:    startTime,
		EndTime:      endTime,
		Environments: arguments.Environments,
		ServiceName:  arguments.ServiceName,
		NodeName:     arguments.NodeName,
	}

	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

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

	response := []PodsResponse{}
	err = json.Unmarshal(resp, &response)
	if err != nil {
		return nil, fmt.Errorf("error unmarshaling response: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprint(response))), nil
}

type PodsResponse struct {
	Name        string `json:"name"`
	Environment string `json:"environment"`
	Status      string `json:"status"`
}

```

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

```go
package tools

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

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

type GetNodesHandlerArgs struct {
	TimeConfig     utils.TimeConfig    `json:"time_config" jsonschema:"required,description=The time period to get nodes for. e.g. if you want to get nodes for the last 5 minutes you would set time_period=5 and time_window=Minutes or if you want to get nodes for the last 2 hours you would set time_period=2 and time_window=Hours. You can also set an absoulute time range by setting start_time and end_time"`
	Filters        map[string][]string `json:"filters" jsonschema:"description=The filters to apply to the nodes. Only the nodes that match these filters will be returned. To get possible filter keys and values use the get_node_attributes tool."`
	ExcludeFilters map[string][]string `json:"excludeFilters" jsonschema:"description=The filters to exclude the nodes. Nodes matching the exclude filters will not be returned. To get possible exclude filter keys and values use the get_node_attributes tool."`
	Environments   []string            `json:"environments" jsonschema:"description=The environments to get nodes that belong to. If empty all environments will be used."`
}

func GetNodesHandler(ctx context.Context, arguments GetNodesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetAllNodesRequest{
		StartTime:      startTime,
		EndTime:        endTime,
		Filters:        arguments.Filters,
		ExcludeFilters: arguments.ExcludeFilters,
		Splits:         []string{},
		Environments:   arguments.Environments,
	}
	body, err := getNodesMetoroCall(ctx, request)
	if err != nil {
		return nil, fmt.Errorf("error getting nodes: %v", err)
	}
	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

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

```

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

```go
package tools

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

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

type GetSourceRepositoryHandlerArgs struct {
	// Required: Service name to get the source repository for
	ServiceName string `json:"serviceName" jsonschema:"required,description=The name of the service to get the source repository for"`

	// Optional: Environment to filter by. If not provided, all environments are considered
	Environments []string `json:"environments" jsonschema:"description=List of environments to search for the service in. If empty all environments will be considered"`

	// Required: Time configuration for the query
	TimeConfig utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time period to get the source repository information for. You can use relative time (e.g. last 5 minutes) or absolute time range."`
}

type GetSourceRepositoryRequest struct {
	ServiceName  string   `json:"serviceName"`
	Environments []string `json:"environments"`
	StartTime    int64    `json:"startTime"`
	EndTime      int64    `json:"endTime"`
}

type GetSourceRepositoryResponse struct {
	// The source repository URL/path found in the deployment
	Repository string `json:"repository"`

	// Whether a repository was found
	Found bool `json:"found"`

	// The environment where the repository information was found
	Environment string `json:"environment,omitempty"`
}

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

	body, err := getSourceRepositoryMetoroCall(ctx, arguments, startTime, endTime)
	if err != nil {
		return nil, fmt.Errorf("error getting source repository: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

func getSourceRepositoryMetoroCall(ctx context.Context, args GetSourceRepositoryHandlerArgs, startTime, endTime int64) ([]byte, error) {
	req := GetSourceRepositoryRequest{
		ServiceName:  args.ServiceName,
		Environments: args.Environments,
		StartTime:    startTime,
		EndTime:      endTime,
	}

	reqBody, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("error marshalling request: %v", err)
	}

	return utils.MakeMetoroAPIRequest("POST", "source/repository", bytes.NewBuffer(reqBody), utils.GetAPIRequirementsFromRequest(ctx))
}

```

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

```go
package tools

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

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

type GetTraceSpansHandlerArgs struct {
	TimeConfig   utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time period to get trace spans for. e.g. if you want to get spans for the last 5 minutes you would set time_period=5 and time_window=Minutes. You can also set an absolute time range by setting start_time and end_time"`
	TraceId      string           `json:"trace_id" jsonschema:"required,description=The traceId of the trace to get the associated spans. get_traces tool will return list of traceIds which should be used for this field."`
	Environments []string         `json:"environments" jsonschema:"description=The environments to get the spans for. If empty all environments will be included"`
}

type GetSpansForTraceRequest struct {
	// Required: Start time of when to get the traces in seconds since epoch
	StartTime int64 `json:"startTime"`
	// Required: End time of when to get the traces in seconds since epoch
	EndTime int64 `json:"endTime"`
	// Required: The traceId of the trace to get the associated spans.
	TraceId string `json:"traceId"`
	// The environments to get the traces for. If empty, all environments will be included
	Environments                   []string `json:"environments"`
	ShouldReturnNonMetoroEpbfSpans bool     `json:"shouldReturnNonMetoroEpbfSpans"`
}

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

	request := GetSpansForTraceRequest{
		StartTime:                      startTime,
		EndTime:                        endTime,
		TraceId:                        arguments.TraceId,
		Environments:                   arguments.Environments,
		ShouldReturnNonMetoroEpbfSpans: true,
	}

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

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

```

--------------------------------------------------------------------------------
/model/model_operator_type.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"
	"fmt"
)

// OperatorType Type of comparison operator
type OperatorType string

// List of OperatorType
const (
	GREATER_THAN          OperatorType = "greaterThan"
	LESS_THAN             OperatorType = "lessThan"
	GREATER_THAN_OR_EQUAL OperatorType = "greaterThanOrEqual"
	LESS_THAN_OR_EQUAL    OperatorType = "lessThanOrEqual"
	EQUALS                OperatorType = "equals"
	NOT_EQUALS            OperatorType = "notEquals"
)

// All allowed values of OperatorType enum
var AllowedOperatorTypeEnumValues = []OperatorType{
	"greaterThan",
	"lessThan",
	"greaterThanOrEqual",
	"lessThanOrEqual",
	"equals",
	"notEquals",
}

func (v *OperatorType) UnmarshalJSON(src []byte) error {
	var value string
	err := json.Unmarshal(src, &value)
	if err != nil {
		return err
	}
	enumTypeValue := OperatorType(value)
	for _, existing := range AllowedOperatorTypeEnumValues {
		if existing == enumTypeValue {
			*v = enumTypeValue
			return nil
		}
	}

	return fmt.Errorf("%+v is not a valid OperatorType", value)
}

// NewOperatorTypeFromValue returns a pointer to a valid OperatorType
// for the value passed as argument, or an error if the value passed is not allowed by the enum
func NewOperatorTypeFromValue(v string) (*OperatorType, error) {
	ev := OperatorType(v)
	if ev.IsValid() {
		return &ev, nil
	} else {
		return nil, fmt.Errorf("invalid value '%v' for OperatorType: valid values are %v", v, AllowedOperatorTypeEnumValues)
	}
}

// IsValid return true if the value is valid for the enum, false otherwise
func (v OperatorType) IsValid() bool {
	for _, existing := range AllowedOperatorTypeEnumValues {
		if existing == v {
			return true
		}
	}
	return false
}

// Ptr returns reference to OperatorType value
func (v OperatorType) Ptr() *OperatorType {
	return &v
}

type NullableOperatorType struct {
	value *OperatorType
	isSet bool
}

func (v NullableOperatorType) Get() *OperatorType {
	return v.value
}

func (v *NullableOperatorType) Set(val *OperatorType) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableOperatorType(val *OperatorType) *NullableOperatorType {
	return &NullableOperatorType{value: val, isSet: true}
}

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

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

```

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

```go
package tools

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

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

type GetK8sEventsVolumeHandlerArgs struct {
	TimeConfig     utils.TimeConfig    `json:"time_config" jsonschema:"required,description=The time period to get events volumes for. e.g. if you want to get events 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"`
	Filters        map[string][]string `json:"filters" jsonschema:"description=Filters to apply to the events. Only the event matching these filters will be counted. Get the possible filter keys from the get_k8s_events_attributes tool and possible filter values from the get_k8s_event_attribute_values tool (for a filter key)"`
	ExcludeFilters map[string][]string `json:"excludeFilters" jsonschema:"description=Filters to exclude the events. Events matching the exclude filters will not be counted. Get the possible exclude filter keys from the get_k8s_events_attributes tool and possible exclude filter values from the get_k8s_event_attribute_values tool (for a key)"`
	Regexes        []string            `json:"regexes" jsonschema:"description=Only the events with messages that match these regexes will be counted"`
	ExcludeRegexes []string            `json:"excludeRegexes" jsonschema:"description=Events with messages that match these regexes will not be counted"`
	Environments   []string            `json:"environments" jsonschema:"description=Environments to get events from"`
}

func GetK8sEventsVolumeHandler(ctx context.Context, arguments GetK8sEventsVolumeHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetK8sEventMetricsRequest{
		StartTime:      startTime,
		EndTime:        endTime,
		Filters:        arguments.Filters,
		ExcludeFilters: arguments.ExcludeFilters,
		Regexes:        arguments.Regexes,
		ExcludeRegexes: arguments.ExcludeRegexes,
		Environments:   arguments.Environments,
		Splits:         []string{"EventType"}, // We want the volume to be split by EventType so we can see the breakdown of Warning/Normal events.
	}

	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

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

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

```

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

```go
package tools

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

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

type GetTracesDistributionHandlerArgs struct {
	TimeConfig     utils.TimeConfig    `json:"time_config" jsonschema:"required,description=The time period to get traces distribution for. e.g. if you want to get traces distribution 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. Try to use a time period 1 hour or less unless its requested."`
	Filters        map[string][]string `json:"filters" jsonschema:"description=Filters to apply to the traces. Only the traces that match these filters will be returned. You have to get the possible filter keys from the get_attribute_keys tool and possible values of a filter key from the get_attribute_values tool. DO NOT GUESS THE FILTER KEYS OR VALUES. Multiple filter keys are ANDed together and values for a filter key are ORed together"`
	ExcludeFilters map[string][]string `json:"excludeFilters" jsonschema:"description=The exclude filters to exclude/eliminate the traces. Traces matching the exclude traces will not be returned. You have to get the possible exclude filter keys from the get_attribute_keys tool and possible value for the key from the get_attribute_values tool. DO NOT GUESS THE FILTER KEYS OR VALUES. Multiple keys are ORed together and values for a filter key are ANDed together"`
}

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

	err = CheckAttributes(ctx, model.Trace, arguments.Filters, arguments.ExcludeFilters, []string{}, nil)
	if err != nil {
		return nil, err
	}

	limit := 20

	request := model.GetTracesRequest{
		StartTime:      startTime,
		EndTime:        endTime,
		Filters:        arguments.Filters,
		ExcludeFilters: arguments.ExcludeFilters,
		Limit:          &limit,
	}

	body, err := getTracesDistributionMetoroCall(ctx, request)
	if err != nil {
		return nil, fmt.Errorf("error getting traces distribution: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
}

func getTracesDistributionMetoroCall(ctx context.Context, request model.GetTracesRequest) ([]byte, error) {
	requestBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling traces distribution request: %v", err)
	}
	return utils.MakeMetoroAPIRequest("POST", "traces/distribution", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
}
```

--------------------------------------------------------------------------------
/resources/resources.go:
--------------------------------------------------------------------------------

```go
package resources

type MetoroResource struct {
	Path        string
	Name        string
	Description string
	ContentType string
	Handler     any
}

var MetoroResourcesList = []MetoroResource{
	{
		Path:        "api://environments",
		Name:        "environments",
		Description: "This resource provides a list of names of the kubernetes clusters/environments monitored by Metoro",
		ContentType: "text/plain",
		Handler:     EnvironmentResourceHandler,
	},
	{
		Path:        "api://namespaces",
		Name:        "namespaces",
		Description: "This resource provides a list of namespaces in the kubernetes clusters/environments monitored by Metoro",
		ContentType: "text/plain",
		Handler:     NamespacesResourceHandler,
	},
	{
		Path:        "api://services",
		Name:        "services",
		Description: "This resource provides a list of services running in the kubernetes clusters/environments monitored by Metoro",
		ContentType: "text/plain",
		Handler:     ServicesResourceHandler,
	},
	{
		Path:        "api://traceAttributes",
		Name:        "traceAttributes",
		Description: "Provides a list of trace attribute keys that are available to be used for filtering or grouping traces. These trace attribute keys should be used as Filter/ExcludeFilter keys or Splits for get_traces, get_trace_metric and get_trace_attribute_values_for_individual_attribute tools arguments.",
		ContentType: "text/plain",
		Handler:     TraceAttributesResourceHandler,
	},
	{
		Path:        "api://k8sEventAttributes",
		Name:        "k8sEventAttributes",
		Description: "Provides a list of Kubernetes Event's attribute keys that are available to be used for filtering or grouping K8s Events. These K8s Event attribute keys should be used as Filter/ExcludeFilter keys or Splits for get_k8s_events, get_k8s_events_volume and get_k8s_events_volume tools arguments.",
		ContentType: "text/plain",
		Handler:     K8sEventsAttributesResourceHandler,
	},
	{
		Path:        "api://metrics",
		Name:        "metricNames",
		Description: "Provides a list of available metric names that can be used for as MetricName arguments to get_metric, get_metric_metadata and get_metric_attributes tools to get metrics data.",
		ContentType: "text/plain",
		Handler:     MetricsResourceHandler,
	},
	{
		Path:        "api://logAttributes",
		Name:        "logAttributes",
		Description: "Provides a list of log attribute keys that are available to be used for filtering or grouping logs. These log attribute keys should be used as Filter/ExcludeFilter keys or Splits for get_logs, get_log_attribute_values_for_individual_attribute tools arguments.",
		ContentType: "text/plain",
		Handler:     LogAttributesResourceHandler,
	},
	{
		Path:        "api://nodes",
		Name:        "nodes",
		Description: "Provides a list of nodes in the kubernetes clusters/environments monitored by Metoro. Any of these nodes/instances can be used as a filter/exclude for get_metric tool with the key 'kubernetes.io/hostname' and value as the node names in this resource.",
		ContentType: "text/plain",
		Handler:     NodesResourceHandler,
	},
}

```

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

```go
package tools

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

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

type GetK8sEventsHandlerArgs struct {
	TimeConfig     utils.TimeConfig    `json:"time_config" jsonschema:"required,description=The time period to get k8s events for. e.g. if you want to get k8s events for the last 6 hours you would set time_period=6 and time_window=Hours. You can also set an absoulute time range by setting start_time and end_time"`
	Filters        map[string][]string `json:"filters" jsonschema:"description=Filters to apply to the events. Only the events that match these filters will be returned. Get the possible filter keys from the get_k8s_events_attributes tool and possible filter values from the get_k8s_event_attribute_values tool (for a filter key)"`
	ExcludeFilters map[string][]string `json:"exclude_filters" jsonschema:"description=Filters to exclude the events. Events matching the exclude filters will not be returned. Get the possible exclude filter keys from the get_k8s_events_attributes tool and possible exclude filter values from the get_k8s_event_attribute_values tool (for a key)"`
	Regexes        []string            `json:"regexes" jsonschema:"description=Regexes to apply to the event messages. Only the events with messages that match these regexes will be returned. Regexes are ORed together. For example if you want to get events with messages that contain the word 'error' or 'warning' you would set the regexes as ['error' 'warning']"`
	ExcludeRegexes []string            `json:"exclude_regexes" jsonschema:"description=Regexes to exclude the events. Events with messages that match these regexes will not be returned. Exclude regexes are AND together. For example if you want to get events with messages that do not contain the word 'error' or 'warning' you would set the exclude regexes as ['error' 'warning']"`
	Environments   []string            `json:"environments" jsonschema:"description=Environments/Clusters to get events for"`
}

func GetK8sEventsHandler(ctx context.Context, arguments GetK8sEventsHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetK8sEventsRequest{
		StartTime:      startTime,
		EndTime:        endTime,
		Filters:        arguments.Filters,
		ExcludeFilters: arguments.ExcludeFilters,
		Regexes:        arguments.Regexes,
		ExcludeRegexes: arguments.ExcludeRegexes,
		Environments:   arguments.Environments,
	}

	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

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

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

```

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

```go
package tools

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	mcpgolang "github.com/metoro-io/mcp-golang"
	"github.com/metoro-io/metoro-mcp-server/model"
	"github.com/metoro-io/metoro-mcp-server/utils"
	"slices"
	"strings"
	"time"
)

type GetAttributeKeysHandlerArgs struct {
	Type       model.MetricType `json:"type" jsonschema:"required,description=The type of attribute keys to get. Either 'logs' or 'trace' or 'metric' or 'kubernetes_resource'"`
	TimeConfig utils.TimeConfig `json:"timeConfig" jsonschema:"required,description=The time period to get the possible attribute keys. e.g. if you want to get the possible values 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"`
	MetricName string           `json:"metricName" jsonschema:"description=The name of the metric to get the possible attribute keys for. This is required if type is 'metric'"`
}

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

	if arguments.Type == model.Metric {
		if arguments.MetricName == "" {
			return nil, fmt.Errorf("metricName is required when type is 'metric'")
		}

		// Check whether the metric is valid. If not, return an error.
		err = CheckMetric(ctx, arguments.MetricName)
		if err != nil {
			return nil, err
		}
	}

	metricAttr := model.GetMetricAttributesRequest{
		StartTime:    startTime,
		EndTime:      endTime,
		MetricName:   arguments.MetricName,
		Environments: []string{}, // TODO: Add environments to the request if needed. For now, we are not using it as I don't think its needed.
	}

	request := model.MultiMetricAttributeKeysRequest{
		Type:   string(arguments.Type),
		Metric: &metricAttr,
	}
	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

	resp, err := utils.MakeMetoroAPIRequest("POST", "metrics/attributes", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))

	if err != nil {
		return nil, fmt.Errorf("error making Metoro call: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

func CheckMetric(ctx context.Context, metricName string) error {
	now := time.Now()
	hourAgo := now.Add(-30 * time.Minute)
	request := model.FuzzyMetricsRequest{
		StartTime:        hourAgo.Unix(),
		EndTime:          now.Unix(),
		MetricFuzzyMatch: "", // This will return all the metric names.
	}
	metricNamesResp, err := getMetricNamesMetoroCall(ctx, request)

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

	metricNamesStr := strings.Join(metricNames.MetricNames, ", ")
	if !slices.Contains(metricNames.MetricNames, metricName) {
		return fmt.Errorf("metricName '%s' is not valid. Valid metric names are: %s", metricName, metricNamesStr)
	}

	return nil
}

```

--------------------------------------------------------------------------------
/model/model_expression_config.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 ExpressionConfig type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &ExpressionConfig{}

// ExpressionConfig struct for ExpressionConfig
type ExpressionConfig struct {
	MetoroQLTimeseries *MetoroQlTimeseries `json:"metoroQLTimeseries,omitempty"`
}

// NewExpressionConfig instantiates a new ExpressionConfig 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 NewExpressionConfig() *ExpressionConfig {
	this := ExpressionConfig{}
	return &this
}

// NewExpressionConfigWithDefaults instantiates a new ExpressionConfig 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 NewExpressionConfigWithDefaults() *ExpressionConfig {
	this := ExpressionConfig{}
	return &this
}

// GetMetoroQLTimeseries returns the MetoroQLTimeseries field value if set, zero value otherwise.
func (o *ExpressionConfig) GetMetoroQLTimeseries() MetoroQlTimeseries {
	if o == nil || IsNil(o.MetoroQLTimeseries) {
		var ret MetoroQlTimeseries
		return ret
	}
	return *o.MetoroQLTimeseries
}

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

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

	return false
}

// SetMetoroQLTimeseries gets a reference to the given MetoroQlTimeseries and assigns it to the MetoroQLTimeseries field.
func (o *ExpressionConfig) SetMetoroQLTimeseries(v MetoroQlTimeseries) {
	o.MetoroQLTimeseries = &v
}

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

func (o ExpressionConfig) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.MetoroQLTimeseries) {
		toSerialize["metoroQLTimeseries"] = o.MetoroQLTimeseries
	}
	return toSerialize, nil
}

type NullableExpressionConfig struct {
	value *ExpressionConfig
	isSet bool
}

func (v NullableExpressionConfig) Get() *ExpressionConfig {
	return v.value
}

func (v *NullableExpressionConfig) Set(val *ExpressionConfig) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableExpressionConfig(val *ExpressionConfig) *NullableExpressionConfig {
	return &NullableExpressionConfig{value: val, isSet: true}
}

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

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

```

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

```go
package tools

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

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

type GetServiceGraphHandlerArgs struct {
	TimeConfig   utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time period to get the service graph for. e.g. if you want to get the graph 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"`
	ServiceName  string           `json:"serviceName" jsonschema:"required,description=The name of the service to get the graph for"`
	Environments []string         `json:"environments" jsonschema:"description=The environments to get the service graph for. If empty all environments will be used."`
}

type GetServiceGraphRequest struct {
	// If Environments is not empty, only services that are in the list will be included in the graph.
	// If Environments is empty, all services will be included in the graph.
	Environments []string `json:"environments"`
	// If InitialServices is not empty, only services that are in the list will be included in the graph.
	// If InitialServices is empty, all services will be included in the graph.
	InitialServices []string `json:"initialServices"`
	// If EndingServices is not empty, only services that are in the list will be included in the graph.
	// If EndingServices is empty, all services will be included in the graph.
	EndingServices []string `json:"endingServices"`
	// StartTime is the start time of the graph in seconds since epoch
	StartTime int64 `json:"startTime"`
	// EndTime is the end time of the graph in seconds since epoch
	EndTime int64 `json:"endTime"`
	// The filters to apply to the traces, 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 filters that should be excluded from the traces
	// For example, if you want to get traces for all services except microservice_a you can pass in
	// {"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"`
}

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

	request := GetServiceGraphRequest{
		StartTime:       startTime,
		EndTime:         endTime,
		Environments:    arguments.Environments,
		InitialServices: []string{arguments.ServiceName},
		EndingServices:  []string{arguments.ServiceName},
	}

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

func getServiceGraphMetoroCall(ctx context.Context, request GetServiceGraphRequest) ([]byte, error) {
	requestBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling service graph request: %v", err)
	}
	return utils.MakeMetoroAPIRequest("POST", "serviceGraph", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
}

```

--------------------------------------------------------------------------------
/model/model_timeseries_specifier_metric.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 TimeseriesSpecifierMetric type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &TimeseriesSpecifierMetric{}

// TimeseriesSpecifierMetric Configuration for a metric timeseries
type TimeseriesSpecifierMetric struct {
	// Name of the metric
	MetricName *string `json:"metricName,omitempty"`
}

// NewTimeseriesSpecifierMetric instantiates a new TimeseriesSpecifierMetric 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 NewTimeseriesSpecifierMetric() *TimeseriesSpecifierMetric {
	this := TimeseriesSpecifierMetric{}
	return &this
}

// NewTimeseriesSpecifierMetricWithDefaults instantiates a new TimeseriesSpecifierMetric 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 NewTimeseriesSpecifierMetricWithDefaults() *TimeseriesSpecifierMetric {
	this := TimeseriesSpecifierMetric{}
	return &this
}

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

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

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

	return false
}

// SetMetricName gets a reference to the given string and assigns it to the MetricName field.
func (o *TimeseriesSpecifierMetric) SetMetricName(v string) {
	o.MetricName = &v
}

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

func (o TimeseriesSpecifierMetric) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.MetricName) {
		toSerialize["metricName"] = o.MetricName
	}
	return toSerialize, nil
}

type NullableTimeseriesSpecifierMetric struct {
	value *TimeseriesSpecifierMetric
	isSet bool
}

func (v NullableTimeseriesSpecifierMetric) Get() *TimeseriesSpecifierMetric {
	return v.value
}

func (v *NullableTimeseriesSpecifierMetric) Set(val *TimeseriesSpecifierMetric) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableTimeseriesSpecifierMetric(val *TimeseriesSpecifierMetric) *NullableTimeseriesSpecifierMetric {
	return &NullableTimeseriesSpecifierMetric{value: val, isSet: true}
}

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

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



```

--------------------------------------------------------------------------------
/utils/time_utils.go:
--------------------------------------------------------------------------------

```go
package utils

import (
	"fmt"
	"os"
	"strings"
	"time"
)

// TimeWindow represents supported time window units
type TimeWindow string

const (
	Minutes TimeWindow = "Minutes"
	Hours   TimeWindow = "Hours"
	Days    TimeWindow = "Days"
)

// TimeRangeType indicates whether the time range is relative or absolute
type TimeRangeType string

const (
	RelativeTimeRange TimeRangeType = "relative"
	AbsoluteTimeRange TimeRangeType = "absolute"
)

// TimeConfig holds the configuration for time range calculation
type TimeConfig struct {
	// Type of time range (relative or absolute)
	Type TimeRangeType `json:"type" jsonschema:"required,enum=relative,enum=absolute,description=Type of time range. Must be either 'relative' or 'absolute'"`

	// Fields for relative time range
	TimePeriod *int        `json:"time_period,omitempty" jsonschema:"description=For relative time range: the number of time units to look back"`
	TimeWindow *TimeWindow `json:"time_window,omitempty" jsonschema:"description=For relative time range: the unit of time (Minutes, Hours, Days)"`

	// Fields for absolute time range
	StartTime *string `json:"start_time,omitempty" jsonschema:"description=For absolute time range: start time in RFC3339 format (e.g., '2024-12-12T14:27:22Z')"`
	EndTime   *string `json:"end_time,omitempty" jsonschema:"description=For absolute time range: end time in RFC3339 format (e.g., '2024-12-12T14:27:22Z')"`
}

// CalculateTimeRange returns start and end timestamps based on the time configuration
func CalculateTimeRange(config TimeConfig) (startTime, endTime int64, err error) {
	now := time.Now()
	thirtyDaysAgo := now.Add(-30 * 24 * time.Hour)

	switch config.Type {
	case RelativeTimeRange:
		if config.TimePeriod == nil || config.TimeWindow == nil {
			return 0, 0, fmt.Errorf("time_period and time_window are required for relative time range")
		}

		var duration time.Duration
		window := strings.ToLower(string(*config.TimeWindow))

		switch window {
		case "minutes", "minute", "min", "mins":
			duration = time.Duration(*config.TimePeriod) * time.Minute
		case "hours", "hour", "hr", "hrs":
			duration = time.Duration(*config.TimePeriod) * time.Hour
		case "days", "day":
			duration = time.Duration(*config.TimePeriod) * 24 * time.Hour
		default:
			return 0, 0, fmt.Errorf("invalid time window: %s", *config.TimeWindow)
		}

		startTimeObj := now.Add(-duration)

		// Check if the start time is more than 30 days ago in Prod.
		if os.Getenv("IS_PROD") == "true" && startTimeObj.Before(thirtyDaysAgo) {
			return 0, 0, fmt.Errorf("time range cannot exceed 30 days ago, please adjust the time_period or time_window")
		}

		return startTimeObj.Unix(), now.Unix(), nil

	case AbsoluteTimeRange:
		if config.StartTime == nil || config.EndTime == nil {
			return 0, 0, fmt.Errorf("start_time and end_time are required for absolute time range")
		}

		startTimeObj, err := time.Parse(time.RFC3339, *config.StartTime)
		if err != nil {
			return 0, 0, fmt.Errorf("invalid start_time format: %v", err)
		}

		endTimeObj, err := time.Parse(time.RFC3339, *config.EndTime)
		if err != nil {
			return 0, 0, fmt.Errorf("invalid end_time format: %v", err)
		}

		if endTimeObj.Before(startTimeObj) {
			return 0, 0, fmt.Errorf("end_time cannot be before start_time")
		}

		// Check if the start time is more than 30 days ago
		if os.Getenv("IS_PROD") == "true" && startTimeObj.Before(thirtyDaysAgo) {
			return 0, 0, fmt.Errorf("time range cannot exceed 30 days")
		}

		return startTimeObj.Unix(), endTimeObj.Unix(), nil

	default:
		return 0, 0, fmt.Errorf("invalid time range type: %s", config.Type)
	}
}

```

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

```go
package tools

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

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

type GetLogAttributeValuesHandlerArgs struct {
	TimeConfig     utils.TimeConfig    `json:"time_config" jsonschema:"required,description=The time period to use while getting the possible values of log attributes. e.g. if you want to get values 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"`
	Attribute      string              `json:"attribute" jsonschema:"required,description=The attribute key to get the possible values for"`
	Filters        map[string][]string `json:"filters" jsonschema:"description=The filters to apply before getting the possible values. For example if you want to get the possible values for attribute key service.name where the environment is X you would set the Filters as {environment: [X]}"`
	ExcludeFilters map[string][]string `json:"excludeFilters" jsonschema:"description=The exclude filters to exclude/eliminate possible values an attribute can take. Log attributes matching the exclude filters will not be returned. For example if you want the possible values for attribute key service.name where the attribute environment is not X then you would set the ExcludeFilters as {environment: [X]}"`
	Regexes        []string            `json:"regexes" jsonschema:"description=The regexes to apply to the log messages. Only the attribute values (for a given attribute key) of logs messages that match these regexes will be returned. For example if you want the possible values for attribute key service.name where the log message contains the word 'error' you would set the regexes as ['error']"`
	ExcludeRegexes []string            `json:"excludeRegexes" jsonschema:"description=The exclude regexes to apply to the log messages. The attribute values (for a given attribute key) of log messages that match these regexes will not be returned. For example if you want the possible values for attribute key service.name where the log message does not contain the word 'error' you would set the exclude regexes as ['error']"`
	Environments   []string            `json:"environments" jsonschema:"description=The environments to get possible values of a log attributes for. If empty, possible values from all environments will be returned"`
}

func GetLogAttributeValuesForIndividualAttributeHandler(ctx context.Context, arguments GetLogAttributeValuesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetSingleLogSummaryRequest{
		LogSummaryRequest: model.LogSummaryRequest{
			StartTime:      startTime,
			EndTime:        endTime,
			Filters:        arguments.Filters,
			ExcludeFilters: arguments.ExcludeFilters,
			Regexes:        arguments.Regexes,
			ExcludeRegexes: arguments.ExcludeRegexes,
			Environments:   arguments.Environments,
		},
		Attribute: arguments.Attribute,
	}

	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

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

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

```

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

```go
package tools

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

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

type GetTraceAttributeValuesHandlerArgs struct {
	TimeConfig     utils.TimeConfig    `json:"time_config" jsonschema:"required,description=The time period to use for getting the possible values for a trace attribute key. e.g. if you want to get possible trace attribute values for key x 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"`
	Attribute      string              `json:"attribute" jsonschema:"required,description=The name of the attribute key to get the possible values for"`
	Filters        map[string][]string `json:"filters" jsonschema:"description=The filters to apply before getting the possible values. For example if you want to get the possible values for attribute key service.name where the environment is X you would set the Filters as {environment: [X]}"`
	ExcludeFilters map[string][]string `json:"excludeFilters" jsonschema:"description=The exclude filters to exclude/eliminate possible values an attribute can take. Traces matching the exclude filters will not be returned. For example if you want the possible values for attribute key service.name where the attribute environment is not X then you would set the ExcludeFilters as {environment: [X]}"`
	Regexes        []string            `json:"regexes" jsonschema:"description=The regexes to apply to the trace endpoint. Only the attribute values (for a given attribute key) of trace endpoint that match these regexes will be returned. For example if you want the possible values for attribute key service.name where the trace endpoint contains the word 'get' you would set the regexes as ['get']"`
	ExcludeRegexes []string            `json:"excludeRegexes" jsonschema:"description=The exclude regexes to apply to the trace endpoint. The attribute values (for a given attribute key) of trace endpoint that match these regexes will not be returned. For example if you want the possible values for attribute key service.name where the trace endpoint does not contain the word 'get' you would set the exclude regexes as ['get']"`
	Environments   []string            `json:"environments" jsonschema:"description=The environments to get traces from. If empty traces from all environments will be returned"`
}

func GetTraceAttributeValuesForIndividualAttributeHandler(ctx context.Context, arguments GetTraceAttributeValuesHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetSingleTraceSummaryRequest{
		TracesSummaryRequest: model.TracesSummaryRequest{
			StartTime:      startTime,
			EndTime:        endTime,
			Filters:        arguments.Filters,
			ExcludeFilters: arguments.ExcludeFilters,
			Regexes:        arguments.Regexes,
			ExcludeRegexes: arguments.ExcludeRegexes,
			Environments:   arguments.Environments,
		},
		Attribute: arguments.Attribute,
	}

	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

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

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

```

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

```go
package tools

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

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

type UpdateInvestigationHandlerArgs struct {
	InvestigationUUID  string           `json:"investigationUuid" jsonschema:"required,description=UUID of the investigation to update"`
	Title              string           `json:"title" jsonschema:"required,description=Title of the investigation"`
	Summary            string           `json:"summary" jsonschema:"description=Summary of the investigation - should be at most 3 sentences"`
	RecommendedActions *[]string        `json:"recommendedActions,omitempty" jsonschema:"description=Optional recommended actions to take to remedy the issue. Should be concise - each item should be a single sentence."`
	ServiceName        *string          `json:"serviceName,omitempty" jsonschema:"description=Optional root cause service name to associate with this investigation."`
	Markdown           string           `json:"markdown" jsonschema:"required,description=Markdown content of the investigation"`
	InProgress         *bool            `json:"inProgress" jsonschema:"description=Whether the investigation is in progress or not. Defaults to false"`
	TimeConfig         utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time period to get the pods for. e.g. if you want the get the pods for the last 5 minutes you would set time_period=5 and time_window=Minutes. You can also set an absolute time range by setting start_time and end_time"`
	ChatHistoryUUID    *string          `json:"chatHistoryUuid,omitempty" jsonschema:"description=Optional chat history UUID to associate with this investigation"`
	IssueUUID          *string          `json:"issueUuid,omitempty" jsonschema:"description=Optional related AI issue UUID for this investigation"`
}

func UpdateInvestigationHandler(ctx context.Context, arguments UpdateInvestigationHandlerArgs) (*mcpgolang.ToolResponse, error) {
	// Create the request body
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}

	falsePtr := false
	reviewRequiredPtr := "ReviewRequired"
	start := time.Unix(startTime, 0)
	end := time.Unix(endTime, 0)

	tags := make(map[string]string)
	if arguments.ServiceName != nil {
		tags["service"] = *arguments.ServiceName
	}

	title := arguments.Title
	summary := arguments.Summary
	markdown := arguments.Markdown
	tagsPtr := tags

	request := model.UpdateInvestigationRequest{
		Title:                &title,
		Summary:              &summary,
		Markdown:             &markdown,
		Tags:                 &tagsPtr,
		IssueStartTime:       &start,
		IssueEndTime:         &end,
		ChatHistoryUUID:      arguments.ChatHistoryUUID,
		IsVisible:            &falsePtr,
		InProgress:           arguments.InProgress,
		MetoroApprovalStatus: &reviewRequiredPtr,
		IssueUUID:            arguments.IssueUUID,
		RecommendedActions:   arguments.RecommendedActions,
	}

	requestBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	// Make the API request - using PUT method for update
	endpoint := fmt.Sprintf("investigation?uuid=%s", arguments.InvestigationUUID)
	responseBody, err := utils.MakeMetoroAPIRequest("PUT", endpoint, bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, fmt.Errorf("failed to update investigation: %w", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
}

```

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

```go
package tools

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

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

type CreateInvestigationHandlerArgs struct {
	Title              string           `json:"title" jsonschema:"required,description=Title of the investigation"`
	Summary            string           `json:"summary" jsonschema:"description=Summary of the investigation - should be at most 3 sentences"`
	RecommendedActions *[]string        `json:"recommendedActions,omitempty" jsonschema:"description=Optional recommended actions to take to remedy the issue. Should be concise - each item should be a single sentence."`
	ServiceName        *string          `json:"serviceName,omitempty" jsonschema:"description=Optional root cause service name to associate with this investigation."`
	Markdown           string           `json:"markdown" jsonschema:"required,description=Markdown content of the investigation"`
	InProgress         *bool            `json:"inProgress" jsonschema:"description=Whether the investigation is in progress or not. Defaults to false"`
	TimeConfig         utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time period to get the pods for. e.g. if you want the get the pods for the last 5 minutes you would set time_period=5 and time_window=Minutes. You can also set an absolute time range by setting start_time and end_time"`
	ChatHistoryUUID    *string          `json:"chatHistoryUuid,omitempty" jsonschema:"description=Optional chat history UUID to associate with this investigation"`
	IssueUUID          *string          `json:"issueUuid,omitempty" jsonschema:"description=Optional related AI issue UUID for this investigation"`
	AlertFireUUID      *string          `json:"alertFireUuid,omitempty" jsonschema:"description=Optional alert fire UUID to associate with this investigation"`
	AlertUUID          *string          `json:"alertUuid,omitempty" jsonschema:"description=Optional alert UUID to associate with this investigation"`
}

func CreateInvestigationHandler(ctx context.Context, arguments CreateInvestigationHandlerArgs) (*mcpgolang.ToolResponse, error) {
	// Create the request body
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}

	falsePtr := false
	reviewRequiredPtr := "ReviewRequired"
	start := time.Unix(startTime, 0)
	end := time.Unix(endTime, 0)
	tags := make(map[string]string)
	if arguments.ServiceName != nil {
		tags["service"] = *arguments.ServiceName
	}
	request := model.CreateInvestigationRequest{
		Title:                arguments.Title,
		Summary:              arguments.Summary,
		RecommendedActions:   arguments.RecommendedActions,
		Markdown:             arguments.Markdown,
		Tags:                 tags,
		IssueStartTime:       &start,
		IssueEndTime:         &end,
		ChatHistoryUUID:      arguments.ChatHistoryUUID,
		IsVisible:            &falsePtr,
		InProgress:           arguments.InProgress,
		MetoroApprovalStatus: &reviewRequiredPtr,
		IssueUUID:            arguments.IssueUUID,
		AlertFireUUID:        arguments.AlertFireUUID,
		AlertUUID:            arguments.AlertUUID,
	}

	requestBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	// Make the API request
	responseBody, err := utils.MakeMetoroAPIRequest("POST", "investigation", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
	if err != nil {
		return nil, fmt.Errorf("failed to create investigation: %w", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
}

```

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

```go
package tools

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

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

type GetK8sEventAttributeValueHandlerArgs struct {
	TimeConfig     utils.TimeConfig    `json:"time_config" jsonschema:"required,description=The time period to get the possible values of K8 event attributes values. e.g. if you want to see the possible values for the attributes in 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"`
	Attribute      string              `json:"attribute" jsonschema:"required,description=The attribute key to get the possible values for"`
	Filters        map[string][]string `json:"filters" jsonschema:"description=The filters to apply before getting the possible values. For example if you want to get the possible values for attribute key service.name where the environment is X you would set the Filters as {environment: [X]}"`
	ExcludeFilters map[string][]string `json:"excludeFilters" jsonschema:"description=The exclude filters to exclude/eliminate possible values an attribute can take. Events matching the exclude filters will not be returned. For example if you want the possible values for attribute key service.name where the attribute environment is not X then you would set the ExcludeFilters as {environment: [X]}"`
	Regexes        []string            `json:"regexes" jsonschema:"description=The regexes to apply to the event messages. Only the attribute values (for a given attribute key) of events messages that match these regexes will be returned. For example if you want the possible values for attribute key service.name where the event message contains the word 'error' you would set the regexes as ['error']"`
	ExcludeRegexes []string            `json:"excludeRegexes" jsonschema:"description=The exclude regexes to apply to the event messages. The attribute values (for a given attribute key) of events messages that match these regexes will not be returned. For example if you want the possible values for attribute key service.name where the event message does not contain the word 'error' you would set the exclude regexes as ['error']"`
	Environments   []string            `json:"environments" jsonschema:"description=The environments to get events from. If empty events from all environments will be returned"`
	Ascending      bool                `json:"ascending" jsonschema:"description=If true events will be returned in ascending order otherwise in descending order"`
}

func GetK8sEventAttributeValuesForIndividualAttributeHandler(ctx context.Context, arguments GetK8sEventAttributeValueHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetSingleK8sEventSummaryRequest{
		GetK8sEventsRequest: model.GetK8sEventsRequest{
			StartTime:      startTime,
			EndTime:        endTime,
			Filters:        arguments.Filters,
			ExcludeFilters: arguments.ExcludeFilters,
			Regexes:        arguments.Regexes,
			ExcludeRegexes: arguments.ExcludeRegexes,
			Environments:   arguments.Environments,
			Ascending:      arguments.Ascending,
		},
		Attribute: arguments.Attribute,
	}

	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

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

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

```

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

```go
package tools

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	mcpgolang "github.com/metoro-io/mcp-golang"
	"github.com/metoro-io/metoro-mcp-server/model"
	"github.com/metoro-io/metoro-mcp-server/utils"
)

type GetAttributeValuesHandlerArgs struct {
	Type       model.MetricType    `json:"type" jsonschema:"required,description=The type of telemetry data to get the attribute keys and values for. Either 'logs' or 'trace' or 'metric' or 'kubernetes_resource'"`
	TimeConfig utils.TimeConfig    `json:"time_config" jsonschema:"required,description=The time period to use while getting the possible values of log attributes. e.g. if you want to get values 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"`
	Attribute  string              `json:"attribute" jsonschema:"required,description=The attribute key to get the possible values for. Possible values for attribute should be obtained from get_attribute_keys tool call for the same type"`
	MetricName string              `json:"metricName" jsonschema:"description=REQUIRED IF THE TYPE IS 'metric'. The name of the metric to get the possible attribute keys and values."`
	Filters    map[string][]string `json:"filters" jsonschema:"description=The filters to apply before getting the possible values. For example if you want to get the possible values for an attribute key where the environment is X you would set the Filters as {environment: [X]}"`
}

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

	request := model.GetAttributeValuesRequest{
		Type:      arguments.Type,
		Attribute: arguments.Attribute,
	}

	switch arguments.Type {
	case model.Logs:
		err = CheckAttributes(ctx, arguments.Type, arguments.Filters, map[string][]string{}, []string{}, nil)
		if err != nil {
			return nil, err
		}
		modelRequest := model.LogSummaryRequest{
			StartTime: startTime,
			EndTime:   endTime,
			Filters:   arguments.Filters,
		}
		request.Logs = &modelRequest
		break
	case model.Trace:
		err = CheckAttributes(ctx, arguments.Type, arguments.Filters, map[string][]string{}, []string{}, nil)
		if err != nil {
			return nil, err
		}
		modelRequest := model.TracesSummaryRequest{
			StartTime: startTime,
			EndTime:   endTime,
			Filters:   arguments.Filters,
		}
		request.Trace = &modelRequest
		break
	case model.Metric:
		err = CheckAttributes(ctx, arguments.Type, arguments.Filters, map[string][]string{}, []string{}, &model.GetMetricAttributesRequest{
			StartTime:  startTime,
			EndTime:    endTime,
			MetricName: arguments.MetricName,
		})
		if err != nil {
			return nil, err
		}
		modelRequest := model.GetMetricAttributesRequest{
			StartTime:    startTime,
			EndTime:      endTime,
			MetricName:   arguments.MetricName,
			Environments: arguments.Filters["environment"],
		}
		request.Metric = &modelRequest
		break
	//case model.KubernetesResource:
	//
	//	modelRequest := model.GetKubernetesResourceRequest{
	//		StartTime:      startTime,
	//		EndTime:        endTime,
	//		Filters:        arguments.Filters,
	//		ExcludeFilters: arguments.Filters,
	//	}
	//	request.Kubernetes = &modelRequest
	//	break
	default:
		return nil, fmt.Errorf("invalid type: %v", arguments.Type)
	}
	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

	resp, err := utils.MakeMetoroAPIRequest("POST", "metrics/attribute/values", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))

	if err != nil {
		return nil, fmt.Errorf("error making Metoro call: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
}

```

--------------------------------------------------------------------------------
/model/model_timeseries_specifier_kubernetes_resource.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 TimeseriesSpecifierKubernetesResource type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &TimeseriesSpecifierKubernetesResource{}

// TimeseriesSpecifierKubernetesResource Configuration for a Kubernetes resource timeseries
type TimeseriesSpecifierKubernetesResource struct {
	// JSONPath expression to extract data from Kubernetes resources
	JsonPath *string `json:"jsonPath,omitempty"`
}

// NewTimeseriesSpecifierKubernetesResource instantiates a new TimeseriesSpecifierKubernetesResource 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 NewTimeseriesSpecifierKubernetesResource() *TimeseriesSpecifierKubernetesResource {
	this := TimeseriesSpecifierKubernetesResource{}
	return &this
}

// NewTimeseriesSpecifierKubernetesResourceWithDefaults instantiates a new TimeseriesSpecifierKubernetesResource 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 NewTimeseriesSpecifierKubernetesResourceWithDefaults() *TimeseriesSpecifierKubernetesResource {
	this := TimeseriesSpecifierKubernetesResource{}
	return &this
}

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

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

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

	return false
}

// SetJsonPath gets a reference to the given string and assigns it to the JsonPath field.
func (o *TimeseriesSpecifierKubernetesResource) SetJsonPath(v string) {
	o.JsonPath = &v
}

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

func (o TimeseriesSpecifierKubernetesResource) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.JsonPath) {
		toSerialize["jsonPath"] = o.JsonPath
	}
	return toSerialize, nil
}

type NullableTimeseriesSpecifierKubernetesResource struct {
	value *TimeseriesSpecifierKubernetesResource
	isSet bool
}

func (v NullableTimeseriesSpecifierKubernetesResource) Get() *TimeseriesSpecifierKubernetesResource {
	return v.value
}

func (v *NullableTimeseriesSpecifierKubernetesResource) Set(val *TimeseriesSpecifierKubernetesResource) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableTimeseriesSpecifierKubernetesResource(val *TimeseriesSpecifierKubernetesResource) *NullableTimeseriesSpecifierKubernetesResource {
	return &NullableTimeseriesSpecifierKubernetesResource{value: val, isSet: true}
}

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

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



```

--------------------------------------------------------------------------------
/model/model_timeseries_config_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"
	"fmt"
	"gopkg.in/validator.v2"
)

// TimeseriesConfigExpression - Expression defining the metrics to monitor
type TimeseriesConfigExpression struct {
	ExpressionConfig *ExpressionConfig
	String *string
}

// ExpressionConfigAsTimeseriesConfigExpression is a convenience function that returns ExpressionConfig wrapped in TimeseriesConfigExpression
func ExpressionConfigAsTimeseriesConfigExpression(v *ExpressionConfig) TimeseriesConfigExpression {
	return TimeseriesConfigExpression{
		ExpressionConfig: v,
	}
}

// stringAsTimeseriesConfigExpression is a convenience function that returns string wrapped in TimeseriesConfigExpression
func StringAsTimeseriesConfigExpression(v *string) TimeseriesConfigExpression {
	return TimeseriesConfigExpression{
		String: v,
	}
}


// Unmarshal JSON data into one of the pointers in the struct
func (dst *TimeseriesConfigExpression) UnmarshalJSON(data []byte) error {
	var err error
	match := 0
	// try to unmarshal data into ExpressionConfig
	err = newStrictDecoder(data).Decode(&dst.ExpressionConfig)
	if err == nil {
		jsonExpressionConfig, _ := json.Marshal(dst.ExpressionConfig)
		if string(jsonExpressionConfig) == "{}" { // empty struct
			dst.ExpressionConfig = nil
		} else {
			if err = validator.Validate(dst.ExpressionConfig); err != nil {
				dst.ExpressionConfig = nil
			} else {
				match++
			}
		}
	} else {
		dst.ExpressionConfig = nil
	}

	// try to unmarshal data into String
	err = newStrictDecoder(data).Decode(&dst.String)
	if err == nil {
		jsonString, _ := json.Marshal(dst.String)
		if string(jsonString) == "{}" { // empty struct
			dst.String = nil
		} else {
			if err = validator.Validate(dst.String); err != nil {
				dst.String = nil
			} else {
				match++
			}
		}
	} else {
		dst.String = nil
	}

	if match > 1 { // more than 1 match
		// reset to nil
		dst.ExpressionConfig = nil
		dst.String = nil

		return fmt.Errorf("data matches more than one schema in oneOf(TimeseriesConfigExpression)")
	} else if match == 1 {
		return nil // exactly one match
	} else { // no match
		return fmt.Errorf("data failed to match schemas in oneOf(TimeseriesConfigExpression)")
	}
}

// Marshal data from the first non-nil pointers in the struct to JSON
func (src TimeseriesConfigExpression) MarshalJSON() ([]byte, error) {
	if src.ExpressionConfig != nil {
		return json.Marshal(&src.ExpressionConfig)
	}

	if src.String != nil {
		return json.Marshal(&src.String)
	}

	return nil, nil // no data in oneOf schemas
}

// Get the actual instance
func (obj *TimeseriesConfigExpression) GetActualInstance() (interface{}) {
	if obj == nil {
		return nil
	}
	if obj.ExpressionConfig != nil {
		return obj.ExpressionConfig
	}

	if obj.String != nil {
		return obj.String
	}

	// all schemas are nil
	return nil
}

// Get the actual instance value
func (obj TimeseriesConfigExpression) GetActualInstanceValue() (interface{}) {
	if obj.ExpressionConfig != nil {
		return *obj.ExpressionConfig
	}

	if obj.String != nil {
		return *obj.String
	}

	// all schemas are nil
	return nil
}

type NullableTimeseriesConfigExpression struct {
	value *TimeseriesConfigExpression
	isSet bool
}

func (v NullableTimeseriesConfigExpression) Get() *TimeseriesConfigExpression {
	return v.value
}

func (v *NullableTimeseriesConfigExpression) Set(val *TimeseriesConfigExpression) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableTimeseriesConfigExpression(val *TimeseriesConfigExpression) *NullableTimeseriesConfigExpression {
	return &NullableTimeseriesConfigExpression{value: val, isSet: true}
}

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

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



```

--------------------------------------------------------------------------------
/model/model_create_update_alert_request.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 CreateUpdateAlertRequest type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &CreateUpdateAlertRequest{}

// CreateUpdateAlertRequest Request to create a new alert or update an existing one. If alert.metadata.id is provided and matches an existing alert, that alert will be updated.
type CreateUpdateAlertRequest struct {
	Alert Alert `json:"alert"`
}

type _CreateUpdateAlertRequest CreateUpdateAlertRequest

// NewCreateUpdateAlertRequest instantiates a new CreateUpdateAlertRequest 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 NewCreateUpdateAlertRequest(alert Alert) *CreateUpdateAlertRequest {
	this := CreateUpdateAlertRequest{}
	this.Alert = alert
	return &this
}

// NewCreateUpdateAlertRequestWithDefaults instantiates a new CreateUpdateAlertRequest 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 NewCreateUpdateAlertRequestWithDefaults() *CreateUpdateAlertRequest {
	this := CreateUpdateAlertRequest{}
	return &this
}

// GetAlert returns the Alert field value
func (o *CreateUpdateAlertRequest) GetAlert() Alert {
	if o == nil {
		var ret Alert
		return ret
	}

	return o.Alert
}

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

// SetAlert sets field value
func (o *CreateUpdateAlertRequest) SetAlert(v Alert) {
	o.Alert = v
}

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

func (o CreateUpdateAlertRequest) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["alert"] = o.Alert
	return toSerialize, nil
}

func (o *CreateUpdateAlertRequest) 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{
		"alert",
	}

	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)
		}
	}

	varCreateUpdateAlertRequest := _CreateUpdateAlertRequest{}

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

	if err != nil {
		return err
	}

	*o = CreateUpdateAlertRequest(varCreateUpdateAlertRequest)

	return err
}

type NullableCreateUpdateAlertRequest struct {
	value *CreateUpdateAlertRequest
	isSet bool
}

func (v NullableCreateUpdateAlertRequest) Get() *CreateUpdateAlertRequest {
	return v.value
}

func (v *NullableCreateUpdateAlertRequest) Set(val *CreateUpdateAlertRequest) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableCreateUpdateAlertRequest(val *CreateUpdateAlertRequest) *NullableCreateUpdateAlertRequest {
	return &NullableCreateUpdateAlertRequest{value: val, isSet: true}
}

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

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

```

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

```go
package tools

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

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

type GetTracesHandlerArgs struct {
	TimeConfig     utils.TimeConfig    `json:"time_config" jsonschema:"required,description=The time period to get traces for. e.g. if you want to get traces 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. Try to use a time period 1 hour or less unless its requested."`
	Filters        map[string][]string `json:"filters" jsonschema:"description=Filters to apply to the traces. Only the traces that match these filters will be returned. You have to get the possible filter keys from the get_attribute_keys tool and possible values of a filter key from the get_attribute_values tool. DO NOT GUESS THE FILTER KEYS OR VALUES. Multiple filter keys are ANDed together and values for a filter key are ORed together"`
	ExcludeFilters map[string][]string `json:"excludeFilters" jsonschema:"description=The exclude filters to exclude/eliminate the traces. Traces matching the exclude traces will not be returned. You have to get the possible exclude filter keys from the get_attribute_keys tool and possible value for the key from the get_attribute_values tool. DO NOT GUESS THE FILTER KEYS OR VALUES. Multiple keys are ORed together and values for a filter key are ANDed together"`
}

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

	err = CheckAttributes(ctx, model.Trace, arguments.Filters, arguments.ExcludeFilters, []string{}, nil)
	if err != nil {
		return nil, err
	}

	limit := 20

	request := model.GetTracesRequest{
		StartTime:      startTime,
		EndTime:        endTime,
		Filters:        arguments.Filters,
		ExcludeFilters: arguments.ExcludeFilters,
		Limit:          &limit,
	}

	body, err := getTracesMetoroCall(ctx, request)
	if err != nil {
		return nil, fmt.Errorf("error getting traces: %v", err)
	}

	// Add human readable duration to the response
	bodyWithDuration, err := addHumanReadableDuration(body)
	if err != nil {
		return nil, fmt.Errorf("error adding human readable duration: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(bodyWithDuration)))), nil
}

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

func addHumanReadableDuration(response []byte) ([]byte, error) {
	var tracesResponse model.GetTracesResponse
	err := json.Unmarshal(response, &tracesResponse)
	if err != nil {
		return nil, fmt.Errorf("error unmarshaling get traces response: %v", err)
	}

	// Add human readable duration to each trace
	for i := range tracesResponse.Traces {
		trace := &tracesResponse.Traces[i]
		durationNs := trace.Duration

		// Convert duration to human readable format
		var humanReadable string
		switch {
		case durationNs < 1000: // Less than 1 microsecond
			humanReadable = fmt.Sprintf("%d nanoseconds", durationNs)
		case durationNs < 1000000: // Less than 1 millisecond
			humanReadable = fmt.Sprintf("%.2f microseconds", float64(durationNs)/1000)
		case durationNs < 1000000000: // Less than 1 second
			humanReadable = fmt.Sprintf("%.2f milliseconds", float64(durationNs)/1000000)
		case durationNs < 60000000000: // Less than 1 minute
			humanReadable = fmt.Sprintf("%.2f seconds", float64(durationNs)/1000000000)
		default: // 1 minute or more
			minutes := durationNs / 60000000000
			seconds := (durationNs % 60000000000) / 1000000000
			humanReadable = fmt.Sprintf("%d minutes %.2f seconds", minutes, float64(seconds))
		}

		// Add human readable duration to span attributes
		trace.DurationReadable = humanReadable
	}

	return json.Marshal(tracesResponse)
}

```

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

```go
package tools

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

	mcpgolang "github.com/metoro-io/mcp-golang"
	"github.com/metoro-io/metoro-mcp-server/model"
	"github.com/metoro-io/metoro-mcp-server/utils"
	"gopkg.in/yaml.v3"
)

type GetVersionForServiceHandlerArgs struct {
	TimeConfig   utils.TimeConfig `json:"time_config" jsonschema:"required,description=The time to get container versions. e.g. if you want to see the versions 5 minutes ago you would set time_period=5 and time_window=Minutes. You can also set an absolute time range by setting start_time and end_time"`
	ServiceName  string           `json:"serviceName" jsonschema:"required,description=The name of the service to get container versions for."`
	Environments []string         `json:"environments" jsonschema:"description=The environments to get service versions for. If empty all environments will be used."`
}

type GetVersionForServiceResponse struct {
	ContainerVersions map[string]map[string]string `json:"container_versions"`
}

type K8sResourceSummaryResponse struct {
	K8sResourceSummary []struct {
		Environment  string `json:"environment"`
		Kind         string `json:"kind"`
		ResourceYaml string `json:"resourceYaml"`
	} `json:"k8sResourceSummary"`
}

func GetVersionForServiceHandler(ctx context.Context, arguments GetVersionForServiceHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetPodsRequest{
		StartTime:    startTime,
		EndTime:      endTime,
		ServiceName:  arguments.ServiceName,
		Environments: arguments.Environments,
	}
	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

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

	// Parse the JSON response
	var summaryResponse K8sResourceSummaryResponse
	err = json.Unmarshal(resp, &summaryResponse)
	if err != nil {
		return nil, fmt.Errorf("error parsing JSON response: %v", err)
	}

	// Extract container versions from each environment
	containerVersions := make(map[string]map[string]string)

	for _, resource := range summaryResponse.K8sResourceSummary {
		// Parse the YAML for each resource
		var yamlData map[string]interface{}
		err = yaml.Unmarshal([]byte(resource.ResourceYaml), &yamlData)
		if err != nil {
			continue // Skip if we can't parse this resource
		}

		// Extract containers for this environment
		envContainers := make(map[string]string)

		// Check for spec.template.spec.containers (Deployment/StatefulSet)
		if spec, ok := yamlData["spec"].(map[string]interface{}); ok {
			if template, ok := spec["template"].(map[string]interface{}); ok {
				if templateSpec, ok := template["spec"].(map[string]interface{}); ok {
					extractContainers(templateSpec, envContainers)
				}
			}
			// Also check spec.containers directly (DaemonSet)
			extractContainers(spec, envContainers)
		}

		if len(envContainers) > 0 {
			containerVersions[resource.Environment] = envContainers
		}
	}

	response := GetVersionForServiceResponse{
		ContainerVersions: containerVersions,
	}

	jsonResponse, err := json.Marshal(response)
	if err != nil {
		return nil, fmt.Errorf("error marshaling response: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(jsonResponse))), nil
}

func extractContainers(spec map[string]interface{}, containerVersions map[string]string) {
	if containers, ok := spec["containers"].([]interface{}); ok {
		for _, container := range containers {
			if containerMap, ok := container.(map[string]interface{}); ok {
				containerName, nameOk := containerMap["name"].(string)
				image, imageOk := containerMap["image"].(string)
				if nameOk && imageOk {
					containerVersions[containerName] = image
				}
			}
		}
	}

	// Also check for init containers
	if initContainers, ok := spec["initContainers"].([]interface{}); ok {
		for _, container := range initContainers {
			if containerMap, ok := container.(map[string]interface{}); ok {
				containerName, nameOk := containerMap["name"].(string)
				image, imageOk := containerMap["image"].(string)
				if nameOk && imageOk {
					containerVersions["init-"+containerName] = image
				}
			}
		}
	}
}

```

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

```go
package tools

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

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

type GetTraceMetricHandlerArgs struct {
	TimeConfig     utils.TimeConfig       `json:"time_config" jsonschema:"required,description=The time period to get the trace timeseries data for. e.g. if you want to get the trace 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"`
	ServiceNames   []string               `json:"serviceNames" jsonschema:"description=Service names to return the trace timeseries data for"`
	Filters        map[string][]string    `json:"filters" jsonschema:"description=The filters to apply to the traces. it is a map of filter keys to array values where array values are ORed.e.g. key for service name is service.name"`
	ExcludeFilters map[string][]string    `json:"excludeFilters" jsonschema:"description=The exclude filters to exclude/eliminate the traces. Traces matching the exclude traces will not be returned. it is a map of filter keys to array values where array values are ORed.e.g. key for service name is service.name"`
	Regexes        []string               `json:"regexes" jsonschema:"description=The regexes to apply to the trace's endpoints. Traces with endpoints matching regexes will be returned"`
	ExcludeRegexes []string               `json:"excludeRegexes" jsonschema:"description=The regexes to exclude from the trace's endpoints. Traces with endpoints matching regexes will be excluded"`
	Splits         []string               `json:"splits" jsonschema:"description=The splits to apply to trace timeseries data. e.g. if you want to split the trace timeseries data by service name you would set splits as ['service.name']. This is useful for seeing a breakdown of the trace timeseries data by an attribute"`
	Functions      []model.MetricFunction `json:"functions" jsonschema:"description=The functions to apply to the traces. 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"`
	Aggregate      string                 `json:"aggregate" jsonschema:"required,description=The aggregation to apply to the metrics. Possible values are: count / p50 / p90 / p95 / p99 / totalSize / responseSize / requestSize. The aggregation will be applied to every datapoint bucket. For example if the bucket size is 1 minute and the aggregation is count then the count of all datapoints in a minute will be returned"`
	Environments   []string               `json:"environments" jsonschema:"description=The environments to get traces from. If empty traces from all environments will be returned"`
}

func GetTraceMetricHandler(ctx context.Context, arguments GetTraceMetricHandlerArgs) (*mcpgolang.ToolResponse, error) {
	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
	if err != nil {
		return nil, fmt.Errorf("error calculating time range: %v", err)
	}
	request := model.GetTraceMetricRequest{
		StartTime:      startTime,
		EndTime:        endTime,
		ServiceNames:   arguments.ServiceNames,
		Filters:        arguments.Filters,
		ExcludeFilters: arguments.ExcludeFilters,
		Regexes:        arguments.Regexes,
		ExcludeRegexes: arguments.ExcludeRegexes,
		Splits:         arguments.Splits,
		Functions:      arguments.Functions,
		Aggregate:      model.Aggregation(arguments.Aggregate),
		Environments:   arguments.Environments,
	}

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

func getTraceMetricMetoroCall(ctx context.Context, request model.GetTraceMetricRequest) ([]byte, error) {
	jsonBody, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("error marshaling request: %v", err)
	}

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

	return resp, nil
}

```

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

```go
package tools

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

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

type GetLogsHandlerArgs struct {
	TimeConfig     utils.TimeConfig    `json:"time_config" jsonschema:"required,description=The time period to get the logs for. e.g. if you want the get the logs 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"`
	Filters        map[string][]string `json:"attributeFilters" jsonschema:"description=You must use get_attribute_keys and get_attribute_values before setting this. Log attributes to restrict the search to. Keys are anded together and values in the keys are ORed.  e.g. {service.name: [/k8s/test/test /k8s/test/test2] namespace:[test]} will return all logs emited from (service.name = /k8s/test/test OR /k8s/test/test2) AND (namespace = test). Get the possible filter keys from the get_attribute_keys tool and possible values of a filter key from the get_attribute_values tool. If you are looking to get logs of a certain severity you should look up the log_level filter."`
	ExcludeFilters map[string][]string `json:"attributeExcludeFilters" jsonschema:"description=You must use get_attribute_keys and get_attribute_values before setting this.Log attributes to exclude from the search. Keys are anded together and values in the keys are ORed.  e.g. {service.name: [/k8s/test/test /k8s/test/test2] namespace:[test]} will return all logs emited from NOT ((service.name = /k8s/test/test OR /k8s/test/test2) AND (namespace = test)). Get the possible filter keys from the get_attribute_keys tool and possible values of a filter key from the get_attribute_values tool. If you are looking to get logs of a certain severity you should look up the log_level filter."`
	Regex          string              `json:"regex" jsonschema:"description=Regex to apply to the log search re2 format. Any match in the log message will cause it to be returned. Use the filters parameter log_level if you want to look for logs of a certain severity"`
	Environments   []string            `json:"environments" jsonschema:"description=The environments to get logs from. If empty logs from all environments will be returned"`
}

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

	var regexes = []string{}
	if arguments.Regex != "" {
		regexes = append(regexes, arguments.Regex)
	}

	err = CheckAttributes(ctx, model.Logs, arguments.Filters, arguments.ExcludeFilters, []string{}, nil)
	if err != nil {
		return nil, err
	}
	limit := 20

	request := model.GetLogsRequest{
		StartTime:      startTime,
		EndTime:        endTime,
		Filters:        arguments.Filters,
		ExcludeFilters: arguments.ExcludeFilters,
		Regexes:        regexes,
		Environments:   arguments.Environments,
		ExportLimit:    &limit,
	}

	resp, err := getLogsMetoroCall(ctx, request)
	if err != nil {
		return nil, fmt.Errorf("error getting logs: %v", err)
	}
	respTrimmed, err := trimLogsResponse(resp)
	if err != nil {
		return nil, fmt.Errorf("error trimming logs: %v", err)
	}

	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(respTrimmed)))), nil
}

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

func trimLogsResponse(response []byte) ([]byte, error) {
	var logsResponse model.GetLogsResponse
	err := json.Unmarshal(response, &logsResponse)
	if err != nil {
		return nil, fmt.Errorf("error unmarshaling logs response: %v", err)
	}

	// Trim every log entry to only include the first 2000 characters of the message
	// This is to prevent excessively long log messages from blowing up the context.
	logLineLengthLimit := 2000
	for i := range logsResponse.Logs {
		if len(logsResponse.Logs[i].Message) > logLineLengthLimit {
			logsResponse.Logs[i].Message = logsResponse.Logs[i].Message[:logLineLengthLimit]
		}
	}

	return json.Marshal(logsResponse)
}

```

--------------------------------------------------------------------------------
/model/model_operator_config.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 OperatorConfig type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &OperatorConfig{}

// OperatorConfig struct for OperatorConfig
type OperatorConfig struct {
	Operator OperatorType `json:"operator"`
	// Threshold value for comparison
	Threshold float64 `json:"threshold"`
}

type _OperatorConfig OperatorConfig

// NewOperatorConfig instantiates a new OperatorConfig 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 NewOperatorConfig(operator OperatorType, threshold float64) *OperatorConfig {
	this := OperatorConfig{}
	this.Operator = operator
	this.Threshold = threshold
	return &this
}

// NewOperatorConfigWithDefaults instantiates a new OperatorConfig 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 NewOperatorConfigWithDefaults() *OperatorConfig {
	this := OperatorConfig{}
	return &this
}

// GetOperator returns the Operator field value
func (o *OperatorConfig) GetOperator() OperatorType {
	if o == nil {
		var ret OperatorType
		return ret
	}

	return o.Operator
}

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

// SetOperator sets field value
func (o *OperatorConfig) SetOperator(v OperatorType) {
	o.Operator = v
}

// GetThreshold returns the Threshold field value
func (o *OperatorConfig) GetThreshold() float64 {
	if o == nil {
		var ret float64
		return ret
	}

	return o.Threshold
}

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

// SetThreshold sets field value
func (o *OperatorConfig) SetThreshold(v float64) {
	o.Threshold = v
}

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

func (o OperatorConfig) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["operator"] = o.Operator
	toSerialize["threshold"] = o.Threshold
	return toSerialize, nil
}

func (o *OperatorConfig) 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{
		"operator",
		"threshold",
	}

	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)
		}
	}

	varOperatorConfig := _OperatorConfig{}

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

	if err != nil {
		return err
	}

	*o = OperatorConfig(varOperatorConfig)

	return err
}

type NullableOperatorConfig struct {
	value *OperatorConfig
	isSet bool
}

func (v NullableOperatorConfig) Get() *OperatorConfig {
	return v.value
}

func (v *NullableOperatorConfig) Set(val *OperatorConfig) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableOperatorConfig(val *OperatorConfig) *NullableOperatorConfig {
	return &NullableOperatorConfig{value: val, isSet: true}
}

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

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

```

--------------------------------------------------------------------------------
/model/model_timeseries_specifier_logs.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 TimeseriesSpecifierLogs type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &TimeseriesSpecifierLogs{}

// TimeseriesSpecifierLogs Configuration for a logs timeseries
type TimeseriesSpecifierLogs struct {
	// Regular expressions to match logs
	Regexes []string `json:"regexes,omitempty"`
	// Regular expressions to exclude logs
	ExcludeRegexes []string `json:"excludeRegexes,omitempty"`
}

// NewTimeseriesSpecifierLogs instantiates a new TimeseriesSpecifierLogs 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 NewTimeseriesSpecifierLogs() *TimeseriesSpecifierLogs {
	this := TimeseriesSpecifierLogs{}
	return &this
}

// NewTimeseriesSpecifierLogsWithDefaults instantiates a new TimeseriesSpecifierLogs 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 NewTimeseriesSpecifierLogsWithDefaults() *TimeseriesSpecifierLogs {
	this := TimeseriesSpecifierLogs{}
	return &this
}

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

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

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

	return false
}

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

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

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

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

	return false
}

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

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

func (o TimeseriesSpecifierLogs) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.Regexes) {
		toSerialize["regexes"] = o.Regexes
	}
	if !IsNil(o.ExcludeRegexes) {
		toSerialize["excludeRegexes"] = o.ExcludeRegexes
	}
	return toSerialize, nil
}

type NullableTimeseriesSpecifierLogs struct {
	value *TimeseriesSpecifierLogs
	isSet bool
}

func (v NullableTimeseriesSpecifierLogs) Get() *TimeseriesSpecifierLogs {
	return v.value
}

func (v *NullableTimeseriesSpecifierLogs) Set(val *TimeseriesSpecifierLogs) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableTimeseriesSpecifierLogs(val *TimeseriesSpecifierLogs) *NullableTimeseriesSpecifierLogs {
	return &NullableTimeseriesSpecifierLogs{value: val, isSet: true}
}

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

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



```

--------------------------------------------------------------------------------
/model/model_action_pager_duty_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 ActionPagerDutyDestination type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &ActionPagerDutyDestination{}

// ActionPagerDutyDestination struct for ActionPagerDutyDestination
type ActionPagerDutyDestination struct {
	// PagerDuty service ID
	ServiceId *string `json:"serviceId,omitempty"`
	// PagerDuty service name
	ServiceName *string `json:"serviceName,omitempty"`
}

// NewActionPagerDutyDestination instantiates a new ActionPagerDutyDestination 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 NewActionPagerDutyDestination() *ActionPagerDutyDestination {
	this := ActionPagerDutyDestination{}
	return &this
}

// NewActionPagerDutyDestinationWithDefaults instantiates a new ActionPagerDutyDestination 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 NewActionPagerDutyDestinationWithDefaults() *ActionPagerDutyDestination {
	this := ActionPagerDutyDestination{}
	return &this
}

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

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

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

	return false
}

// SetServiceId gets a reference to the given string and assigns it to the ServiceId field.
func (o *ActionPagerDutyDestination) SetServiceId(v string) {
	o.ServiceId = &v
}

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

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

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

	return false
}

// SetServiceName gets a reference to the given string and assigns it to the ServiceName field.
func (o *ActionPagerDutyDestination) SetServiceName(v string) {
	o.ServiceName = &v
}

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

func (o ActionPagerDutyDestination) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.ServiceId) {
		toSerialize["serviceId"] = o.ServiceId
	}
	if !IsNil(o.ServiceName) {
		toSerialize["serviceName"] = o.ServiceName
	}
	return toSerialize, nil
}

type NullableActionPagerDutyDestination struct {
	value *ActionPagerDutyDestination
	isSet bool
}

func (v NullableActionPagerDutyDestination) Get() *ActionPagerDutyDestination {
	return v.value
}

func (v *NullableActionPagerDutyDestination) Set(val *ActionPagerDutyDestination) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableActionPagerDutyDestination(val *ActionPagerDutyDestination) *NullableActionPagerDutyDestination {
	return &NullableActionPagerDutyDestination{value: val, isSet: true}
}

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

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

```

--------------------------------------------------------------------------------
/model/model_timeseries_specifier_traces.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 TimeseriesSpecifierTraces type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &TimeseriesSpecifierTraces{}

// TimeseriesSpecifierTraces Configuration for a traces timeseries
type TimeseriesSpecifierTraces struct {
	// Regular expressions to match traces
	Regexes []string `json:"regexes,omitempty"`
	// Regular expressions to exclude traces
	ExcludeRegexes []string `json:"excludeRegexes,omitempty"`
}

// NewTimeseriesSpecifierTraces instantiates a new TimeseriesSpecifierTraces 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 NewTimeseriesSpecifierTraces() *TimeseriesSpecifierTraces {
	this := TimeseriesSpecifierTraces{}
	return &this
}

// NewTimeseriesSpecifierTracesWithDefaults instantiates a new TimeseriesSpecifierTraces 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 NewTimeseriesSpecifierTracesWithDefaults() *TimeseriesSpecifierTraces {
	this := TimeseriesSpecifierTraces{}
	return &this
}

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

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

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

	return false
}

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

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

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

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

	return false
}

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

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

func (o TimeseriesSpecifierTraces) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.Regexes) {
		toSerialize["regexes"] = o.Regexes
	}
	if !IsNil(o.ExcludeRegexes) {
		toSerialize["excludeRegexes"] = o.ExcludeRegexes
	}
	return toSerialize, nil
}

type NullableTimeseriesSpecifierTraces struct {
	value *TimeseriesSpecifierTraces
	isSet bool
}

func (v NullableTimeseriesSpecifierTraces) Get() *TimeseriesSpecifierTraces {
	return v.value
}

func (v *NullableTimeseriesSpecifierTraces) Set(val *TimeseriesSpecifierTraces) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableTimeseriesSpecifierTraces(val *TimeseriesSpecifierTraces) *NullableTimeseriesSpecifierTraces {
	return &NullableTimeseriesSpecifierTraces{value: val, isSet: true}
}

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

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



```

--------------------------------------------------------------------------------
/model/model_action_email_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 ActionEmailDestination type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &ActionEmailDestination{}

// ActionEmailDestination struct for ActionEmailDestination
type ActionEmailDestination struct {
	// Whether to notify the email recipients when the alert is resolved
	NotifyOnResolve *bool `json:"notifyOnResolve,omitempty"`
	// Email addresses to send alert to
	Emails []string `json:"emails,omitempty"`
}

// NewActionEmailDestination instantiates a new ActionEmailDestination 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 NewActionEmailDestination() *ActionEmailDestination {
	this := ActionEmailDestination{}
	var notifyOnResolve bool = false
	this.NotifyOnResolve = &notifyOnResolve
	return &this
}

// NewActionEmailDestinationWithDefaults instantiates a new ActionEmailDestination 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 NewActionEmailDestinationWithDefaults() *ActionEmailDestination {
	this := ActionEmailDestination{}
	var notifyOnResolve bool = false
	this.NotifyOnResolve = &notifyOnResolve
	return &this
}

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

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

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

	return false
}

// SetNotifyOnResolve gets a reference to the given bool and assigns it to the NotifyOnResolve field.
func (o *ActionEmailDestination) SetNotifyOnResolve(v bool) {
	o.NotifyOnResolve = &v
}

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

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

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

	return false
}

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

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

func (o ActionEmailDestination) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.NotifyOnResolve) {
		toSerialize["notifyOnResolve"] = o.NotifyOnResolve
	}
	if !IsNil(o.Emails) {
		toSerialize["emails"] = o.Emails
	}
	return toSerialize, nil
}

type NullableActionEmailDestination struct {
	value *ActionEmailDestination
	isSet bool
}

func (v NullableActionEmailDestination) Get() *ActionEmailDestination {
	return v.value
}

func (v *NullableActionEmailDestination) Set(val *ActionEmailDestination) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableActionEmailDestination(val *ActionEmailDestination) *NullableActionEmailDestination {
	return &NullableActionEmailDestination{value: val, isSet: true}
}

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

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



```

--------------------------------------------------------------------------------
/model/model_action_slack_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 ActionSlackDestination type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &ActionSlackDestination{}

// ActionSlackDestination struct for ActionSlackDestination
type ActionSlackDestination struct {
	// Slack channel to send alert to
	Channel *string `json:"channel,omitempty"`
	// Whether to notify the channel when the alert is resolved
	NotifyOnResolve *bool `json:"notifyOnResolve,omitempty"`
}

// NewActionSlackDestination instantiates a new ActionSlackDestination 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 NewActionSlackDestination() *ActionSlackDestination {
	this := ActionSlackDestination{}
	var notifyOnResolve bool = false
	this.NotifyOnResolve = &notifyOnResolve
	return &this
}

// NewActionSlackDestinationWithDefaults instantiates a new ActionSlackDestination 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 NewActionSlackDestinationWithDefaults() *ActionSlackDestination {
	this := ActionSlackDestination{}
	var notifyOnResolve bool = false
	this.NotifyOnResolve = &notifyOnResolve
	return &this
}

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

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

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

	return false
}

// SetChannel gets a reference to the given string and assigns it to the Channel field.
func (o *ActionSlackDestination) SetChannel(v string) {
	o.Channel = &v
}

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

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

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

	return false
}

// SetNotifyOnResolve gets a reference to the given bool and assigns it to the NotifyOnResolve field.
func (o *ActionSlackDestination) SetNotifyOnResolve(v bool) {
	o.NotifyOnResolve = &v
}

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

func (o ActionSlackDestination) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	if !IsNil(o.Channel) {
		toSerialize["channel"] = o.Channel
	}
	if !IsNil(o.NotifyOnResolve) {
		toSerialize["notifyOnResolve"] = o.NotifyOnResolve
	}
	return toSerialize, nil
}

type NullableActionSlackDestination struct {
	value *ActionSlackDestination
	isSet bool
}

func (v NullableActionSlackDestination) Get() *ActionSlackDestination {
	return v.value
}

func (v *NullableActionSlackDestination) Set(val *ActionSlackDestination) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableActionSlackDestination(val *ActionSlackDestination) *NullableActionSlackDestination {
	return &NullableActionSlackDestination{value: val, isSet: true}
}

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

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



```

--------------------------------------------------------------------------------
/model/model_metoro_ql_timeseries.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 MetoroQlTimeseries type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &MetoroQlTimeseries{}

// MetoroQlTimeseries struct for MetoroQlTimeseries
type MetoroQlTimeseries struct {
	// MetoroQL query to use for the timeseries.
	Query string `json:"query"`
	// The size of each bucket to aggregate data into in seconds. For example, if the bucket size is 60, the data will be aggregated into 1 minute buckets.
	BucketSize int64 `json:"bucketSize"`
}

type _MetoroQlTimeseries MetoroQlTimeseries

// NewMetoroQlTimeseries instantiates a new MetoroQlTimeseries 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 NewMetoroQlTimeseries(query string, bucketSize int64) *MetoroQlTimeseries {
	this := MetoroQlTimeseries{}
	this.Query = query
	this.BucketSize = bucketSize
	return &this
}

// NewMetoroQlTimeseriesWithDefaults instantiates a new MetoroQlTimeseries 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 NewMetoroQlTimeseriesWithDefaults() *MetoroQlTimeseries {
	this := MetoroQlTimeseries{}
	return &this
}

// GetQuery returns the Query field value
func (o *MetoroQlTimeseries) GetQuery() string {
	if o == nil {
		var ret string
		return ret
	}

	return o.Query
}

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

// SetQuery sets field value
func (o *MetoroQlTimeseries) SetQuery(v string) {
	o.Query = v
}

// GetBucketSize returns the BucketSize field value
func (o *MetoroQlTimeseries) GetBucketSize() int64 {
	if o == nil {
		var ret int64
		return ret
	}

	return o.BucketSize
}

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

// SetBucketSize sets field value
func (o *MetoroQlTimeseries) SetBucketSize(v int64) {
	o.BucketSize = v
}

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

func (o MetoroQlTimeseries) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["query"] = o.Query
	toSerialize["bucketSize"] = o.BucketSize
	return toSerialize, nil
}

func (o *MetoroQlTimeseries) 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{
		"query",
		"bucketSize",
	}

	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)
		}
	}

	varMetoroQlTimeseries := _MetoroQlTimeseries{}

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

	if err != nil {
		return err
	}

	*o = MetoroQlTimeseries(varMetoroQlTimeseries)

	return err
}

type NullableMetoroQlTimeseries struct {
	value *MetoroQlTimeseries
	isSet bool
}

func (v NullableMetoroQlTimeseries) Get() *MetoroQlTimeseries {
	return v.value
}

func (v *NullableMetoroQlTimeseries) Set(val *MetoroQlTimeseries) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableMetoroQlTimeseries(val *MetoroQlTimeseries) *NullableMetoroQlTimeseries {
	return &NullableMetoroQlTimeseries{value: val, isSet: true}
}

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

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

```

--------------------------------------------------------------------------------
/model/model_timeseries_config.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 TimeseriesConfig type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &TimeseriesConfig{}

// TimeseriesConfig struct for TimeseriesConfig
type TimeseriesConfig struct {
	Expression ExpressionConfig `json:"expression"`
	// Rules for evaluating the metrics and triggering alerts
	EvaluationRules []Condition `json:"evaluationRules"`
}

type _TimeseriesConfig TimeseriesConfig

// NewTimeseriesConfig instantiates a new TimeseriesConfig 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 NewTimeseriesConfig(expression ExpressionConfig, evaluationRules []Condition) *TimeseriesConfig {
	this := TimeseriesConfig{}
	this.Expression = expression
	this.EvaluationRules = evaluationRules
	return &this
}

// NewTimeseriesConfigWithDefaults instantiates a new TimeseriesConfig 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 NewTimeseriesConfigWithDefaults() *TimeseriesConfig {
	this := TimeseriesConfig{}
	return &this
}

// GetExpression returns the Expression field value
func (o *TimeseriesConfig) GetExpression() ExpressionConfig {
	if o == nil {
		var ret ExpressionConfig
		return ret
	}

	return o.Expression
}

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

// SetExpression sets field value
func (o *TimeseriesConfig) SetExpression(v ExpressionConfig) {
	o.Expression = v
}

// GetEvaluationRules returns the EvaluationRules field value
func (o *TimeseriesConfig) GetEvaluationRules() []Condition {
	if o == nil {
		var ret []Condition
		return ret
	}

	return o.EvaluationRules
}

// GetEvaluationRulesOk returns a tuple with the EvaluationRules field value
// and a boolean to check if the value has been set.
func (o *TimeseriesConfig) GetEvaluationRulesOk() ([]Condition, bool) {
	if o == nil {
		return nil, false
	}
	return o.EvaluationRules, true
}

// SetEvaluationRules sets field value
func (o *TimeseriesConfig) SetEvaluationRules(v []Condition) {
	o.EvaluationRules = v
}

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

func (o TimeseriesConfig) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["expression"] = o.Expression
	toSerialize["evaluationRules"] = o.EvaluationRules
	return toSerialize, nil
}

func (o *TimeseriesConfig) 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{
		"expression",
		"evaluationRules",
	}

	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)
		}
	}

	varTimeseriesConfig := _TimeseriesConfig{}

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

	if err != nil {
		return err
	}

	*o = TimeseriesConfig(varTimeseriesConfig)

	return err
}

type NullableTimeseriesConfig struct {
	value *TimeseriesConfig
	isSet bool
}

func (v NullableTimeseriesConfig) Get() *TimeseriesConfig {
	return v.value
}

func (v *NullableTimeseriesConfig) Set(val *TimeseriesConfig) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableTimeseriesConfig(val *TimeseriesConfig) *NullableTimeseriesConfig {
	return &NullableTimeseriesConfig{value: val, isSet: true}
}

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

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

```

--------------------------------------------------------------------------------
/model/model_static_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 StaticCondition type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &StaticCondition{}

// StaticCondition struct for StaticCondition
type StaticCondition struct {
	// List of operator conditions that must be met
	Operators           []OperatorConfig    `json:"operators"`
	PersistenceSettings PersistenceSettings `json:"persistenceSettings"`
}

type _StaticCondition StaticCondition

// NewStaticCondition instantiates a new StaticCondition 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 NewStaticCondition(operators []OperatorConfig, persistenceSettings PersistenceSettings) *StaticCondition {
	this := StaticCondition{}
	this.Operators = operators
	this.PersistenceSettings = persistenceSettings
	return &this
}

// NewStaticConditionWithDefaults instantiates a new StaticCondition 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 NewStaticConditionWithDefaults() *StaticCondition {
	this := StaticCondition{}
	return &this
}

// GetOperators returns the Operators field value
func (o *StaticCondition) GetOperators() []OperatorConfig {
	if o == nil {
		var ret []OperatorConfig
		return ret
	}

	return o.Operators
}

// GetOperatorsOk returns a tuple with the Operators field value
// and a boolean to check if the value has been set.
func (o *StaticCondition) GetOperatorsOk() ([]OperatorConfig, bool) {
	if o == nil {
		return nil, false
	}
	return o.Operators, true
}

// SetOperators sets field value
func (o *StaticCondition) SetOperators(v []OperatorConfig) {
	o.Operators = v
}

// GetPersistenceSettings returns the PersistenceSettings field value
func (o *StaticCondition) GetPersistenceSettings() PersistenceSettings {
	if o == nil {
		var ret PersistenceSettings
		return ret
	}

	return o.PersistenceSettings
}

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

// SetPersistenceSettings sets field value
func (o *StaticCondition) SetPersistenceSettings(v PersistenceSettings) {
	o.PersistenceSettings = v
}

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

func (o StaticCondition) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["operators"] = o.Operators
	toSerialize["persistenceSettings"] = o.PersistenceSettings
	return toSerialize, nil
}

func (o *StaticCondition) 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{
		"operators",
		"persistenceSettings",
	}

	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)
		}
	}

	varStaticCondition := _StaticCondition{}

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

	if err != nil {
		return err
	}

	*o = StaticCondition(varStaticCondition)

	return err
}

type NullableStaticCondition struct {
	value *StaticCondition
	isSet bool
}

func (v NullableStaticCondition) Get() *StaticCondition {
	return v.value
}

func (v *NullableStaticCondition) Set(val *StaticCondition) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableStaticCondition(val *StaticCondition) *NullableStaticCondition {
	return &NullableStaticCondition{value: val, isSet: true}
}

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

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

```

--------------------------------------------------------------------------------
/model/model_alert.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 Alert type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &Alert{}

// Alert struct for Alert
type Alert struct {
	Metadata   MetadataObject   `json:"metadata"`
	Type       *AlertType       `json:"type,omitempty"`
	Timeseries TimeseriesConfig `json:"timeseries"`
}

type _Alert Alert

// NewAlert instantiates a new Alert 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 NewAlert(metadata MetadataObject, timeseries TimeseriesConfig) *Alert {
	this := Alert{}
	this.Metadata = metadata
	this.Timeseries = timeseries
	return &this
}

// NewAlertWithDefaults instantiates a new Alert 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 NewAlertWithDefaults() *Alert {
	this := Alert{}
	return &this
}

// GetMetadata returns the Metadata field value
func (o *Alert) GetMetadata() MetadataObject {
	if o == nil {
		var ret MetadataObject
		return ret
	}

	return o.Metadata
}

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

// SetMetadata sets field value
func (o *Alert) SetMetadata(v MetadataObject) {
	o.Metadata = v
}

// GetType returns the Type field value if set, zero value otherwise.
func (o *Alert) GetType() AlertType {
	if o == nil || IsNil(o.Type) {
		var ret AlertType
		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 *Alert) GetTypeOk() (*AlertType, 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 *Alert) HasType() bool {
	if o != nil && !IsNil(o.Type) {
		return true
	}

	return false
}

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

// GetTimeseries returns the Timeseries field value
func (o *Alert) GetTimeseries() TimeseriesConfig {
	if o == nil {
		var ret TimeseriesConfig
		return ret
	}

	return o.Timeseries
}

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

// SetTimeseries sets field value
func (o *Alert) SetTimeseries(v TimeseriesConfig) {
	o.Timeseries = v
}

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

func (o Alert) ToMap() (map[string]interface{}, error) {
	toSerialize := map[string]interface{}{}
	toSerialize["metadata"] = o.Metadata
	if !IsNil(o.Type) {
		toSerialize["type"] = o.Type
	}
	toSerialize["timeseries"] = o.Timeseries
	return toSerialize, nil
}

func (o *Alert) 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{
		"metadata",
		"timeseries",
	}

	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)
		}
	}

	varAlert := _Alert{}

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

	if err != nil {
		return err
	}

	*o = Alert(varAlert)

	return err
}

type NullableAlert struct {
	value *Alert
	isSet bool
}

func (v NullableAlert) Get() *Alert {
	return v.value
}

func (v *NullableAlert) Set(val *Alert) {
	v.value = val
	v.isSet = true
}

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

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

func NewNullableAlert(val *Alert) *NullableAlert {
	return &NullableAlert{value: val, isSet: true}
}

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

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

```
Page 1/2FirstPrevNextLast