#
tokens: 49633/50000 69/98 files (page 1/3)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 3. Use http://codebase.md/metoro-io/metoro-mcp-server?lines=true&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:
--------------------------------------------------------------------------------

```
1 | .idea/*
2 | metoro-mcp-server/metoro-mcp-server
3 | 
```

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

```yaml
 1 | version: 2
 2 | 
 3 | before:
 4 |   hooks:
 5 |     - go mod tidy
 6 | 
 7 | builds:
 8 |   - main: ./main.go
 9 |     env:
10 |       - CGO_ENABLED=0
11 |     goos:
12 |       - linux
13 |       - windows
14 |       - darwin
15 |     goarch:
16 |       - amd64
17 |       - arm64
18 |     mod_timestamp: '{{ .CommitTimestamp }}'
19 |     flags:
20 |       - -trimpath
21 |     ldflags:
22 |       - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }}
23 | 
24 | archives:
25 |   - format: tar.gz
26 |     name_template: >-
27 |       {{ .ProjectName }}_
28 |       {{- title .Os }}_
29 |       {{- if eq .Arch "amd64" }}x86_64
30 |       {{- else }}{{ .Arch }}{{ end }}
31 |     format_overrides:
32 |       - goos: windows
33 |         format: zip
34 | 
35 | changelog:
36 |   sort: asc
37 |   filters:
38 |     exclude:
39 |       - '^docs:'
40 |       - '^test:'
41 |       - '^ci:'
42 |       - Merge pull request
43 |       - Merge branch
44 | 
```

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

```markdown
 1 | <div align="center">
 2 | <img src="./images/Metoro_square.svg" height="300" alt="Metoro MCP Logo">
 3 | </div>
 4 | <br/>
 5 | <div align="center">
 6 | 
 7 | ![GitHub stars](https://img.shields.io/github/stars/metoro-io/metoro-mcp-server?style=social)
 8 | ![GitHub forks](https://img.shields.io/github/forks/metoro-io/metoro-mcp-server?style=social)
 9 | ![GitHub issues](https://img.shields.io/github/issues/metoro-io/metoro-mcp-server)
10 | ![GitHub pull requests](https://img.shields.io/github/issues-pr/metoro-io/metoro-mcp-server)
11 | ![GitHub license](https://img.shields.io/github/license/metoro-io/metoro-mcp-server)
12 | ![GitHub contributors](https://img.shields.io/github/contributors/metoro-io/metoro-mcp-server)
13 | ![GitHub last commit](https://img.shields.io/github/last-commit/metoro-io/metoro-mcp-server)
14 | [![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)
15 | [![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)
16 | ![Tests](https://github.com/metoro-io/metoro-mcp-server/actions/workflows/go-test.yml/badge.svg)
17 | 
18 | </div>
19 | 
20 | # metoro-mcp-server
21 | 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!
22 | 
23 | ## What is MCP (Model Context Protocol)? 
24 | You can read more about the Model Context Protocol here: https://modelcontextprotocol.io
25 | 
26 | But in a nutshell
27 | > 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.
28 | 
29 | ## What is Metoro?
30 | [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.
31 | 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.
32 | 
33 | This MCP server exposes those APIs to an LLM so you can ask your AI questions about your Kubernetes cluster.
34 | 
35 | ## Demo
36 | 
37 | https://github.com/user-attachments/assets/b3f21e9a-45b8-4c17-8d8c-cff560d8694f
38 | 
39 | ## How can I use Metoro MCP Server? 
40 | 1. Install the [Claude Desktop App](https://claude.ai/download).
41 | 2. Make sure you have [Golang](https://golang.org/dl/) installed. `brew install go` for mac or `sudo apt-get install golang` for ubuntu.
42 | 3. Clone the repository: `git clone https://github.com/metoro-io/metoro-mcp-server.git`
43 | 4. Navigate to the repository directory: `cd metoro-mcp-server`
44 | 5. Build the server executable: `go build -o metoro-mcp-server`
45 | 
46 | ### If you already have a Metoro Account:
47 | Copy your auth token from your Metoro account in [Settings](https://us-east.metoro.io/settings) -> Users Settings. 
48 | Create a file in `~/Library/Application Support/Claude/claude_desktop_config.json` with the following contents:
49 | ```json
50 | {
51 |   "mcpServers": {
52 |     "metoro-mcp-server": {
53 |       "command": "<your path to Metoro MCP server go executable>/metoro-mcp-server",
54 |       "args": [],
55 |       "env": {
56 |           "METORO_AUTH_TOKEN" : "<your auth token>",
57 |           "METORO_API_URL": "https://us-east.metoro.io"
58 |        }
59 |     }
60 |   }
61 | }
62 | ```
63 | 
64 | ### If you don't have a Metoro Account:
65 | No worries, you can still play around using the [Live Demo Cluster](https://demo.us-east.metoro.io/).
66 | The included token is a demo token, publicly available for anyone to use.
67 |    Create a file in `~/Library/Application Support/Claude/claude_desktop_config.json` with the following contents:
68 | ```json
69 | {
70 |   "mcpServers": {
71 |     "metoro-mcp-server": {
72 |       "command": "<your path to Metoro MCP server go executable>/metoro-mcp-server",
73 |       "args": [],
74 |       "env": {
75 |           "METORO_AUTH_TOKEN" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjdXN0b21lcklkIjoiOThlZDU1M2QtYzY4ZC00MDRhLWFhZjItNDM2ODllNWJiMGUzIiwiZW1haWwiOiJ0ZXN0QGNocmlzYmF0dGFyYmVlLmNvbSIsImV4cCI6MTgyMTI0NzIzN30.7G6alDpcZh_OThYj293Jce5rjeOBqAhOlANR_Fl5auw",
76 |           "METORO_API_URL": "https://demo.us-east.metoro.io"
77 |        }
78 |     }
79 |   }
80 | }
81 | ```
82 | 
83 | 4. Once you are done editing `claude_desktop_config.json` save the file and restart Claude Desktop app.
84 | 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!
85 | 
86 | ## Built with
87 | 
88 | This server is built on top of our [Golang MCP SDK](https://github.com/metoro-io/mcp-golang).
89 | 
```

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

```yaml
 1 | name: Go Test
 2 | 
 3 | on:
 4 |   push:
 5 |     branches: [ main ]
 6 |   pull_request:
 7 |     branches: [ main ]
 8 | 
 9 | jobs:
10 |   test:
11 |     runs-on: ubuntu-latest
12 |     steps:
13 |       - uses: actions/checkout@v4
14 | 
15 |       - name: Set up Go
16 |         uses: actions/setup-go@v4
17 |         with:
18 |           go-version: '>=1.23'
19 |           cache: false
20 | 
21 |       - name: Run tests
22 |         run: go test -v ./...
23 | 
```

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

```go
 1 | package resources
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 7 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 8 | )
 9 | 
10 | func ServicesResourceHandler() (*mcpgolang.ResourceResponse, error) {
11 | 	ctx := context.Background()
12 | 	response, err := utils.MakeMetoroAPIRequest("GET", "services", nil, utils.GetAPIRequirementsFromRequest(ctx))
13 | 	if err != nil {
14 | 		return nil, err
15 | 	}
16 | 
17 | 	return mcpgolang.NewResourceResponse(
18 | 		mcpgolang.NewTextEmbeddedResource("api://services", string(response), "text/plain")), nil
19 | }
20 | 
```

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

```go
 1 | package resources
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 7 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 8 | )
 9 | 
10 | func NamespacesResourceHandler() (*mcpgolang.ResourceResponse, error) {
11 | 	ctx := context.Background()
12 | 	response, err := utils.MakeMetoroAPIRequest("GET", "namespaces", nil, utils.GetAPIRequirementsFromRequest(ctx))
13 | 	if err != nil {
14 | 		return nil, err
15 | 	}
16 | 
17 | 	return mcpgolang.NewResourceResponse(
18 | 		mcpgolang.NewTextEmbeddedResource("api://namespaces", string(response), "text/plain")), nil
19 | }
20 | 
```

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

```go
 1 | package resources
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 7 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 8 | )
 9 | 
10 | func EnvironmentResourceHandler() (*mcpgolang.ResourceResponse, error) {
11 | 	ctx := context.Background()
12 | 	response, err := utils.MakeMetoroAPIRequest("GET", "environments", nil, utils.GetAPIRequirementsFromRequest(ctx))
13 | 	if err != nil {
14 | 		return nil, err
15 | 	}
16 | 
17 | 	return mcpgolang.NewResourceResponse(
18 | 		mcpgolang.NewTextEmbeddedResource("api://environments", string(response), "text/plain")), nil
19 | 
20 | }
21 | 
```

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

```go
 1 | package resources
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 7 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 8 | )
 9 | 
10 | func LogAttributesResourceHandler() (*mcpgolang.ResourceResponse, error) {
11 | 	ctx := context.Background()
12 | 	resp, err := utils.MakeMetoroAPIRequest("GET", "logsSummaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
13 | 	if err != nil {
14 | 		return nil, err
15 | 	}
16 | 
17 | 	return mcpgolang.NewResourceResponse(
18 | 		mcpgolang.NewTextEmbeddedResource("api://logAttributes", string(resp), "text/plain")), nil
19 | }
20 | 
```

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

```go
 1 | package resources
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 7 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 8 | )
 9 | 
10 | func TraceAttributesResourceHandler() (*mcpgolang.ResourceResponse, error) {
11 | 	ctx := context.Background()
12 | 	resp, err := utils.MakeMetoroAPIRequest("GET", "tracesSummaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
13 | 	if err != nil {
14 | 		return nil, err
15 | 	}
16 | 
17 | 	return mcpgolang.NewResourceResponse(
18 | 		mcpgolang.NewTextEmbeddedResource("api://traceAttributes", string(resp), "text/plain")), nil
19 | }
20 | 
```

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

```go
 1 | package resources
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 7 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 8 | )
 9 | 
10 | func K8sEventsAttributesResourceHandler() (*mcpgolang.ResourceResponse, error) {
11 | 	ctx := context.Background()
12 | 	resp, err := utils.MakeMetoroAPIRequest("GET", "k8s/events/summaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
13 | 	if err != nil {
14 | 		return nil, err
15 | 	}
16 | 
17 | 	return mcpgolang.NewResourceResponse(
18 | 		mcpgolang.NewTextEmbeddedResource("api://k8sEventAttributes", string(resp), "text/plain")), nil
19 | }
20 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 9 | )
10 | 
11 | type GetTraceAttributesHandlerArgs struct{}
12 | 
13 | func GetTraceAttributesHandler(ctx context.Context, arguments GetTraceAttributesHandlerArgs) (*mcpgolang.ToolResponse, error) {
14 | 	resp, err := utils.MakeMetoroAPIRequest("GET", "tracesSummaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
15 | 	if err != nil {
16 | 		return nil, err
17 | 	}
18 | 
19 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
20 | }
21 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 9 | )
10 | 
11 | type GetK8sEventsAttributesHandlerArgs struct{}
12 | 
13 | func GetK8sEventsAttributesHandler(ctx context.Context, arguments GetK8sEventsAttributesHandlerArgs) (*mcpgolang.ToolResponse, error) {
14 | 	resp, err := utils.MakeMetoroAPIRequest("GET", "k8s/events/summaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
15 | 	if err != nil {
16 | 		return nil, err
17 | 	}
18 | 
19 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
20 | }
21 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 9 | )
10 | 
11 | type GetLogAttributesHandlerArgs struct{}
12 | 
13 | func GetLogAttributesHandler(ctx context.Context, arguments GetLogAttributesHandlerArgs) (*mcpgolang.ToolResponse, error) {
14 | 	resp, err := utils.MakeMetoroAPIRequest("GET", "logsSummaryAttributes", nil, utils.GetAPIRequirementsFromRequest(ctx))
15 | 	if err != nil {
16 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
17 | 	}
18 | 
19 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
20 | }
21 | 
```

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

```yaml
 1 | name: Release with GoReleaser
 2 | 
 3 | on:
 4 |   push:
 5 |     tags:
 6 |       - 'v*'
 7 | 
 8 | permissions:
 9 |   contents: write
10 | 
11 | jobs:
12 |   release:
13 |     runs-on: ubuntu-latest
14 |     steps:
15 |       - name: Checkout
16 |         uses: actions/checkout@v4
17 |         with:
18 |           fetch-depth: 0
19 |       
20 |       - name: Set up Go
21 |         uses: actions/setup-go@v4
22 |         with:
23 |           go-version: '>=1.23'
24 |           cache: false
25 |           
26 |       - name: Run GoReleaser
27 |         uses: goreleaser/goreleaser-action@v6
28 |         with:
29 |           distribution: goreleaser
30 |           version: latest
31 |           args: release --clean
32 |         env:
33 |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 9 | )
10 | 
11 | type GetAlertHandlerArgs struct{}
12 | 
13 | func GetAlertsHandler(ctx context.Context, arguments GetAlertHandlerArgs) (*mcpgolang.ToolResponse, error) {
14 | 	body, err := getAlertsMetoroCall(ctx)
15 | 	if err != nil {
16 | 		return nil, fmt.Errorf("error getting alerts: %v", err)
17 | 	}
18 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
19 | }
20 | 
21 | func getAlertsMetoroCall(ctx context.Context) ([]byte, error) {
22 | 	return utils.MakeMetoroAPIRequest("GET", "searchAlerts", nil, utils.GetAPIRequirementsFromRequest(ctx))
23 | }
24 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 9 | )
10 | 
11 | type GetServicesHandlerArgs struct{}
12 | 
13 | func GetServicesHandler(ctx context.Context, arguments GetServicesHandlerArgs) (*mcpgolang.ToolResponse, error) {
14 | 	body, err := getServicesMetoroCall(ctx)
15 | 	if err != nil {
16 | 		return nil, fmt.Errorf("error getting services: %v", err)
17 | 	}
18 | 
19 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
20 | }
21 | 
22 | func getServicesMetoroCall(ctx context.Context) ([]byte, error) {
23 | 	return utils.MakeMetoroAPIRequest("GET", "services", nil, utils.GetAPIRequirementsFromRequest(ctx))
24 | }
25 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 9 | )
10 | 
11 | type GetNamespacesHandlerArgs struct{}
12 | 
13 | func GetNamespacesHandler(ctx context.Context, arguments GetNamespacesHandlerArgs) (*mcpgolang.ToolResponse, error) {
14 | 	body, err := getNamespacesMetoroCall(ctx)
15 | 	if err != nil {
16 | 		return nil, fmt.Errorf("error getting namespaces: %v", err)
17 | 	}
18 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
19 | }
20 | 
21 | func getNamespacesMetoroCall(ctx context.Context) ([]byte, error) {
22 | 	return utils.MakeMetoroAPIRequest("GET", "namespaces", nil, utils.GetAPIRequirementsFromRequest(ctx))
23 | }
24 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 9 | )
10 | 
11 | type GetEnvironmentHandlerArgs struct{}
12 | 
13 | func GetEnvironmentsHandler(ctx context.Context, arguments GetEnvironmentHandlerArgs) (*mcpgolang.ToolResponse, error) {
14 | 	body, err := getEnvironmentsMetoroCall(ctx)
15 | 	if err != nil {
16 | 		return nil, fmt.Errorf("error getting environments: %v", err)
17 | 	}
18 | 
19 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
20 | }
21 | 
22 | func getEnvironmentsMetoroCall(ctx context.Context) ([]byte, error) {
23 | 	return utils.MakeMetoroAPIRequest("GET", "environments", nil, utils.GetAPIRequirementsFromRequest(ctx))
24 | }
25 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 9 | )
10 | 
11 | type ListAIIssueHandlerArgs struct {
12 | 	OpenOnly *bool `json:"openOnly,omitempty" jsonschema:"description=Set to true to list only open issues (default true), false to list all issues"`
13 | }
14 | 
15 | func ListAIIssuesHandler(ctx context.Context, arguments ListAIIssueHandlerArgs) (*mcpgolang.ToolResponse, error) {
16 | 	openOnly := true
17 | 	if arguments.OpenOnly != nil {
18 | 		openOnly = *arguments.OpenOnly
19 | 	}
20 | 
21 | 	endpoint := "aiIssues"
22 | 	if openOnly {
23 | 		endpoint += "?openOnly=true"
24 | 	}
25 | 
26 | 	responseBody, err := utils.MakeMetoroAPIRequest("GET", endpoint, nil, utils.GetAPIRequirementsFromRequest(ctx))
27 | 	if err != nil {
28 | 		return nil, fmt.Errorf("failed to list AI issues: %w", err)
29 | 	}
30 | 
31 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
32 | }
33 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 9 | )
10 | 
11 | type GetMetricMetadataHandlerArgs struct {
12 | 	Name string `json:"name" jsonschema:"required,description=The name of the metric to get metadata for"`
13 | }
14 | 
15 | func GetMetricMetadata(ctx context.Context, arguments GetMetricMetadataHandlerArgs) (*mcpgolang.ToolResponse, error) {
16 | 	response, err := getMetricMetadataMetoroCall(ctx, arguments.Name)
17 | 	if err != nil {
18 | 		return nil, fmt.Errorf("error calling Metoro get metric metadata api: %v", err)
19 | 	}
20 | 
21 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(response)))), nil
22 | }
23 | 
24 | func getMetricMetadataMetoroCall(ctx context.Context, metricName string) ([]byte, error) {
25 | 	return utils.MakeMetoroAPIRequest("GET", "metric/metadata?name="+metricName, nil, utils.GetAPIRequirementsFromRequest(ctx))
26 | }
27 | 
```

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

```go
 1 | package resources
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"time"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | func MetricsResourceHandler() (*mcpgolang.ResourceResponse, error) {
15 | 	now := time.Now()
16 | 	twoHoursAgo := now.Add(-2 * time.Hour)
17 | 	request := model.FuzzyMetricsRequest{
18 | 		StartTime:        twoHoursAgo.Unix(),
19 | 		EndTime:          now.Unix(),
20 | 		MetricFuzzyMatch: "",         // This will return all the metric names.
21 | 		Environments:     []string{}, // All environments
22 | 	}
23 | 	jsonData, err := json.Marshal(request)
24 | 	if err != nil {
25 | 		return nil, err
26 | 	}
27 | 	ctx := context.Background()
28 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "fuzzyMetricsNames", bytes.NewBuffer(jsonData), utils.GetAPIRequirementsFromRequest(ctx))
29 | 	if err != nil {
30 | 		return nil, err
31 | 	}
32 | 	return mcpgolang.NewResourceResponse(
33 | 		mcpgolang.NewTextEmbeddedResource("api://metrics", string(resp), "text/plain")), nil
34 | }
35 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 	"time"
 7 | 
 8 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 9 | 	"github.com/metoro-io/metoro-mcp-server/utils"
10 | )
11 | 
12 | type GetNodeInfoHandlerArgs struct {
13 | 	NodeName string `json:"nodeName" jsonschema:"required,description=The name of the node to get the YAML/information for"`
14 | }
15 | 
16 | func GetNodeInfoHandler(ctx context.Context, arguments GetNodeInfoHandlerArgs) (*mcpgolang.ToolResponse, error) {
17 | 	now := time.Now()
18 | 	fiveMinsAgo := now.Add(-10 * time.Minute)
19 | 
20 | 	body, err := getNodeInfoMetoroCall(ctx, arguments.NodeName, fiveMinsAgo.Unix())
21 | 	if err != nil {
22 | 		return nil, fmt.Errorf("error getting node info: %v", err)
23 | 	}
24 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
25 | }
26 | 
27 | func getNodeInfoMetoroCall(ctx context.Context, nodeName string, startTime int64) ([]byte, error) {
28 | 	return utils.MakeMetoroAPIRequest("GET", fmt.Sprintf("infrastructure/node?nodeName=%s&startTime=%d", nodeName, startTime), nil, utils.GetAPIRequirementsFromRequest(ctx))
29 | }
30 | 
```

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

```go
 1 | package resources
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"time"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | func NodesResourceHandler() (*mcpgolang.ResourceResponse, error) {
15 | 	now := time.Now()
16 | 	tenMinsAgo := now.Add(-10 * time.Minute)
17 | 	request := model.GetAllNodesRequest{
18 | 		StartTime:      tenMinsAgo.Unix(),
19 | 		EndTime:        now.Unix(),
20 | 		Filters:        map[string][]string{},
21 | 		ExcludeFilters: map[string][]string{},
22 | 		Splits:         []string{},
23 | 		Environments:   []string{},
24 | 	}
25 | 	jsonRequest, err := json.Marshal(request)
26 | 	if err != nil {
27 | 		return nil, err
28 | 	}
29 | 	ctx := context.Background()
30 | 	response, err := utils.MakeMetoroAPIRequest("POST", "infrastructure/nodes", bytes.NewBuffer(jsonRequest), utils.GetAPIRequirementsFromRequest(ctx))
31 | 	if err != nil {
32 | 		return nil, err
33 | 	}
34 | 	return mcpgolang.NewResourceResponse(
35 | 		mcpgolang.NewTextEmbeddedResource("api://nodes", string(response), "text/plain")), nil
36 | }
37 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/model"
 9 | 	"github.com/metoro-io/metoro-mcp-server/utils"
10 | )
11 | 
12 | type GetNodeAttributesHandlerArgs struct {
13 | 	TimeConfig utils.TimeConfig `json:"timeConfig" jsonschema:"required,description=The time range to get the node attributes for"`
14 | }
15 | 
16 | func GetNodeAttributesHandler(ctx context.Context, arguments GetNodeAttributesHandlerArgs) (*mcpgolang.ToolResponse, error) {
17 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
18 | 	if err != nil {
19 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
20 | 	}
21 | 	request := model.MetricAttributesRequest{
22 | 		StartTime:        startTime,
23 | 		EndTime:          endTime,
24 | 		MetricName:       "node_info",
25 | 		FilterAttributes: map[string][]string{},
26 | 	}
27 | 	response, err := getMetricAttributesMetoroCall(ctx, request)
28 | 	if err != nil {
29 | 		return nil, fmt.Errorf("error calling Metoro API: %v", err)
30 | 	}
31 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(response)))), nil
32 | }
33 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"encoding/json"
 6 | 	"fmt"
 7 | 
 8 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 9 | 	"github.com/metoro-io/metoro-mcp-server/model"
10 | 	"github.com/metoro-io/metoro-mcp-server/utils"
11 | )
12 | 
13 | type GetAIIssueHandlerArgs struct {
14 | 	IssueUUID string `json:"issueUuid" jsonschema:"required,description=UUID of the AI issue to retrieve"`
15 | }
16 | 
17 | func GetAIIssueHandler(ctx context.Context, arguments GetAIIssueHandlerArgs) (*mcpgolang.ToolResponse, error) {
18 | 	endpoint := fmt.Sprintf("aiIssue?uuid=%s", arguments.IssueUUID)
19 | 	responseBody, err := utils.MakeMetoroAPIRequest("GET", endpoint, nil, utils.GetAPIRequirementsFromRequest(ctx))
20 | 	if err != nil {
21 | 		return nil, fmt.Errorf("failed to fetch AI issue: %w", err)
22 | 	}
23 | 
24 | 	var issueResponse model.GetAIIssueResponse
25 | 	if err := json.Unmarshal(responseBody, &issueResponse); err != nil {
26 | 		return nil, fmt.Errorf("failed to parse AI issue response: %w", err)
27 | 	}
28 | 
29 | 	serialized, err := json.Marshal(issueResponse.Issue)
30 | 	if err != nil {
31 | 		return nil, fmt.Errorf("failed to marshal AI issue: %w", err)
32 | 	}
33 | 
34 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(serialized))), nil
35 | }
36 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"encoding/json"
 6 | 	"fmt"
 7 | 
 8 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 9 | 	"github.com/metoro-io/metoro-mcp-server/model"
10 | 	"github.com/metoro-io/metoro-mcp-server/utils"
11 | )
12 | 
13 | type ListAIIssueEventsHandlerArgs struct {
14 | 	IssueUUID string `json:"issueUuid" jsonschema:"required,description=UUID of the AI issue whose events should be listed"`
15 | }
16 | 
17 | func ListAIIssueEventsHandler(ctx context.Context, arguments ListAIIssueEventsHandlerArgs) (*mcpgolang.ToolResponse, error) {
18 | 	endpoint := fmt.Sprintf("aiIssue/events?issueUuid=%s", arguments.IssueUUID)
19 | 	responseBody, err := utils.MakeMetoroAPIRequest("GET", endpoint, nil, utils.GetAPIRequirementsFromRequest(ctx))
20 | 	if err != nil {
21 | 		return nil, fmt.Errorf("failed to list AI issue events: %w", err)
22 | 	}
23 | 
24 | 	var resp model.ListAIIssueEventsResponse
25 | 	if err := json.Unmarshal(responseBody, &resp); err != nil {
26 | 		return nil, fmt.Errorf("failed to parse AI issue events response: %w", err)
27 | 	}
28 | 
29 | 	serialized, err := json.Marshal(resp.Events)
30 | 	if err != nil {
31 | 		return nil, fmt.Errorf("failed to marshal AI issue events: %w", err)
32 | 	}
33 | 
34 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(serialized))), nil
35 | }
36 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type CreateAIIssueHandlerArgs struct {
15 | 	Title       string `json:"title" jsonschema:"required,description=Title of the AI issue"`
16 | 	Description string `json:"description" jsonschema:"required,description=Detailed description of the AI issue"`
17 | 	Summary     string `json:"summary" jsonschema:"required,description=One sentence summary of the AI issue"`
18 | }
19 | 
20 | func CreateAIIssueHandler(ctx context.Context, arguments CreateAIIssueHandlerArgs) (*mcpgolang.ToolResponse, error) {
21 | 	request := model.CreateAIIssueRequest{
22 | 		Title:       arguments.Title,
23 | 		Description: arguments.Description,
24 | 		Summary:     arguments.Summary,
25 | 	}
26 | 
27 | 	requestBody, err := json.Marshal(request)
28 | 	if err != nil {
29 | 		return nil, fmt.Errorf("failed to marshal request: %w", err)
30 | 	}
31 | 
32 | 	responseBody, err := utils.MakeMetoroAPIRequest("POST", "aiIssue", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
33 | 	if err != nil {
34 | 		return nil, fmt.Errorf("failed to create AI issue: %w", err)
35 | 	}
36 | 
37 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
38 | }
39 | 
```

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

```go
 1 | package main
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"os"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/mcp-golang/transport/stdio"
 9 | 	"github.com/metoro-io/metoro-mcp-server/resources"
10 | 	"github.com/metoro-io/metoro-mcp-server/tools"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | func main() {
15 | 	// Check if the appropriate environment variables are set
16 | 	if err := checkEnvVars(); err != nil {
17 | 		panic(err)
18 | 	}
19 | 
20 | 	done := make(chan struct{})
21 | 
22 | 	mcpServer := mcpgolang.NewServer(stdio.NewStdioServerTransport())
23 | 
24 | 	// Add tools
25 | 	for _, tool := range tools.MetoroToolsList {
26 | 		err := mcpServer.RegisterTool(tool.Name, tool.Description, tool.Handler)
27 | 		if err != nil {
28 | 			panic(err)
29 | 		}
30 | 	}
31 | 
32 | 	// Add resources
33 | 	for _, resource := range resources.MetoroResourcesList {
34 | 		err := mcpServer.RegisterResource(
35 | 			resource.Path,
36 | 			resource.Name,
37 | 			resource.Description,
38 | 			resource.ContentType,
39 | 			resource.Handler)
40 | 		if err != nil {
41 | 			panic(err)
42 | 		}
43 | 	}
44 | 
45 | 	err := mcpServer.Serve()
46 | 	if err != nil {
47 | 		panic(err)
48 | 	}
49 | 
50 | 	<-done
51 | }
52 | 
53 | func checkEnvVars() error {
54 | 	if os.Getenv(utils.METORO_API_URL_ENV_VAR) == "" {
55 | 		return fmt.Errorf("%s environment variable not set", utils.METORO_API_URL_ENV_VAR)
56 | 	}
57 | 	if os.Getenv(utils.METORO_AUTH_TOKEN_ENV_VAR) == "" {
58 | 		return fmt.Errorf("%s environment variable not set", utils.METORO_AUTH_TOKEN_ENV_VAR)
59 | 	}
60 | 	return nil
61 | }
62 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 	"time"
 9 | 
10 | 	mcpgolang "github.com/metoro-io/mcp-golang"
11 | 	"github.com/metoro-io/metoro-mcp-server/model"
12 | 	"github.com/metoro-io/metoro-mcp-server/utils"
13 | )
14 | 
15 | type GetMetricNamesHandlerArgs struct {
16 | 	Environments []string `json:"environments" jsonschema:"description=Environments to get metrics names from. If empty all environments will be used."`
17 | }
18 | 
19 | func GetMetricNamesHandler(ctx context.Context, arguments GetMetricNamesHandlerArgs) (*mcpgolang.ToolResponse, error) {
20 | 	now := time.Now()
21 | 	hourAgo := now.Add(-1 * time.Hour)
22 | 	request := model.FuzzyMetricsRequest{
23 | 		StartTime:        hourAgo.Unix(),
24 | 		EndTime:          now.Unix(),
25 | 		MetricFuzzyMatch: "", // This will return all the metric names.
26 | 		Environments:     arguments.Environments,
27 | 	}
28 | 	response, err := getMetricNamesMetoroCall(ctx, request)
29 | 	if err != nil {
30 | 		return nil, fmt.Errorf("error calling Metoro API: %v", err)
31 | 	}
32 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(response)))), nil
33 | }
34 | 
35 | func getMetricNamesMetoroCall(ctx context.Context, request model.FuzzyMetricsRequest) ([]byte, error) {
36 | 	jsonData, err := json.Marshal(request)
37 | 	if err != nil {
38 | 		return nil, err
39 | 	}
40 | 	return utils.MakeMetoroAPIRequest("POST", "fuzzyMetricsNames", bytes.NewBuffer(jsonData), utils.GetAPIRequirementsFromRequest(ctx))
41 | }
42 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 
 7 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 8 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 9 | )
10 | 
11 | type GetAlertFiresHandlerArgs struct {
12 | 	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"`
13 | 	AlertId    string           `json:"alert_id" jsonschema:"required,description=The ID of the alert to get the alert fires for"`
14 | }
15 | 
16 | func GetAlertFiresHandler(ctx context.Context, arguments GetAlertFiresHandlerArgs) (*mcpgolang.ToolResponse, error) {
17 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
18 | 	if err != nil {
19 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
20 | 	}
21 | 	body, err := getAlertFiresMetoroCall(ctx, arguments.AlertId, startTime, endTime)
22 | 	if err != nil {
23 | 		return nil, fmt.Errorf("error getting alert fires: %v", err)
24 | 	}
25 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
26 | }
27 | 
28 | func getAlertFiresMetoroCall(ctx context.Context, alertId string, startTime, endTime int64) ([]byte, error) {
29 | 	return utils.MakeMetoroAPIRequest("GET", fmt.Sprintf("alertFires?alertId=%s&startTime=%d&endTime=%d", alertId, startTime, endTime), nil, utils.GetAPIRequirementsFromRequest(ctx))
30 | }
31 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 	"time"
 7 | 
 8 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 9 | )
10 | 
11 | type UnixToRFC3339HandlerArgs struct {
12 | 	UnixTimestamp int64 `json:"unix_timestamp" jsonschema:"required,description=Unix timestamp in seconds or milliseconds"`
13 | }
14 | 
15 | func UnixToRFC3339Handler(ctx context.Context, arguments UnixToRFC3339HandlerArgs) (*mcpgolang.ToolResponse, error) {
16 | 	// Determine if the timestamp is in seconds or milliseconds
17 | 	// Unix timestamps in seconds are typically 10 digits (until year 2286)
18 | 	// Unix timestamps in milliseconds are typically 13 digits
19 | 	var t time.Time
20 | 
21 | 	// Check if it's likely milliseconds (more than 10 digits or would result in a date far in the future)
22 | 	if arguments.UnixTimestamp > 9999999999 {
23 | 		// Treat as milliseconds
24 | 		t = time.Unix(0, arguments.UnixTimestamp*int64(time.Millisecond))
25 | 	} else {
26 | 		// Treat as seconds
27 | 		t = time.Unix(arguments.UnixTimestamp, 0)
28 | 	}
29 | 
30 | 	// Convert to RFC3339 format
31 | 	rfc3339String := t.UTC().Format(time.RFC3339)
32 | 
33 | 	// Create a response with both interpretations if the timestamp could be ambiguous
34 | 	var response string
35 | 	if arguments.UnixTimestamp <= 9999999999 && arguments.UnixTimestamp >= 1000000000 {
36 | 		// Could be either seconds or milliseconds, show both
37 | 		tAsMillis := time.Unix(0, arguments.UnixTimestamp*int64(time.Millisecond))
38 | 		response = fmt.Sprintf("Interpreted as seconds: %s\nInterpreted as milliseconds: %s",
39 | 			rfc3339String,
40 | 			tAsMillis.UTC().Format(time.RFC3339))
41 | 	} else {
42 | 		response = rfc3339String
43 | 	}
44 | 
45 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(response)), nil
46 | }
47 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	"github.com/google/uuid"
10 | 	mcpgolang "github.com/metoro-io/mcp-golang"
11 | 	"github.com/metoro-io/metoro-mcp-server/model"
12 | 	"github.com/metoro-io/metoro-mcp-server/utils"
13 | )
14 | 
15 | type CreateDashboardHandlerArgs struct {
16 | 	DashboardName string            `json:"dashboard_name" jsonschema:"required,description=The name of the dashboard to create"`
17 | 	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"`
18 | }
19 | 
20 | func CreateDashboardHandler(ctx context.Context, arguments CreateDashboardHandlerArgs) (*mcpgolang.ToolResponse, error) {
21 | 	dashboardJson, err := json.Marshal(arguments.GroupWidget)
22 | 	if err != nil {
23 | 		return nil, fmt.Errorf("error marshaling dashboard properties: %v", err)
24 | 	}
25 | 
26 | 	newDashboardRequest := model.SetDashboardRequest{
27 | 		Name:             arguments.DashboardName,
28 | 		Id:               uuid.NewString(),
29 | 		DashboardJson:    string(dashboardJson),
30 | 		DefaultTimeRange: "1h",
31 | 	}
32 | 
33 | 	resp, err := setDashboardMetoroCall(ctx, newDashboardRequest)
34 | 	if err != nil {
35 | 		return nil, fmt.Errorf("error setting dashboard: %v", err)
36 | 	}
37 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
38 | }
39 | 
40 | func setDashboardMetoroCall(ctx context.Context, request model.SetDashboardRequest) ([]byte, error) {
41 | 	requestBody, err := json.Marshal(request)
42 | 	if err != nil {
43 | 		return nil, fmt.Errorf("error marshaling dashboard request: %v", err)
44 | 	}
45 | 	return utils.MakeMetoroAPIRequest("POST", "dashboard", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
46 | }
47 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetK8sServiceInformationHandlerArgs struct {
15 | 	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"`
16 | 	ServiceName  string           `json:"serviceName" jsonschema:"required,description=The name of the service to get YAML file for."`
17 | 	Environments []string         `json:"environments" jsonschema:"description=The environments to get service YAML for. If empty all environments will be used."`
18 | }
19 | 
20 | func GetK8sServiceInformationHandler(ctx context.Context, arguments GetK8sServiceInformationHandlerArgs) (*mcpgolang.ToolResponse, error) {
21 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
22 | 	if err != nil {
23 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
24 | 	}
25 | 	request := model.GetPodsRequest{
26 | 		StartTime:    startTime,
27 | 		EndTime:      endTime,
28 | 		ServiceName:  arguments.ServiceName,
29 | 		Environments: arguments.Environments,
30 | 	}
31 | 	jsonBody, err := json.Marshal(request)
32 | 	if err != nil {
33 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
34 | 	}
35 | 
36 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "k8s/summary", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
37 | 	if err != nil {
38 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
39 | 	}
40 | 
41 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
42 | }
43 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type UpdateAIIssueHandlerArgs struct {
15 | 	IssueUUID   string  `json:"issueUuid" jsonschema:"required,description=UUID of the AI issue to update"`
16 | 	Title       *string `json:"title,omitempty" jsonschema:"description=Optional new title for the AI issue"`
17 | 	Description *string `json:"description,omitempty" jsonschema:"description=Optional new description for the AI issue"`
18 | 	Summary     *string `json:"summary,omitempty" jsonschema:"description=Optional new summary for the AI issue"`
19 | 	Open        *bool   `json:"open,omitempty" jsonschema:"description=Optional flag to set whether the AI issue is open (true) or resolved (false)"`
20 | }
21 | 
22 | func UpdateAIIssueHandler(ctx context.Context, arguments UpdateAIIssueHandlerArgs) (*mcpgolang.ToolResponse, error) {
23 | 	if arguments.Title == nil && arguments.Description == nil && arguments.Summary == nil && arguments.Open == nil {
24 | 		return nil, fmt.Errorf("at least one of title, description, summary, or open must be provided to update an AI issue")
25 | 	}
26 | 
27 | 	request := model.UpdateAIIssueRequest{
28 | 		Title:       arguments.Title,
29 | 		Description: arguments.Description,
30 | 		Summary:     arguments.Summary,
31 | 		Open:        arguments.Open,
32 | 	}
33 | 
34 | 	requestBody, err := json.Marshal(request)
35 | 	if err != nil {
36 | 		return nil, fmt.Errorf("failed to marshal request: %w", err)
37 | 	}
38 | 
39 | 	endpoint := fmt.Sprintf("aiIssue?uuid=%s", arguments.IssueUUID)
40 | 	responseBody, err := utils.MakeMetoroAPIRequest("PUT", endpoint, bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
41 | 	if err != nil {
42 | 		return nil, fmt.Errorf("failed to update AI issue: %w", err)
43 | 	}
44 | 
45 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
46 | }
47 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetProfileHandlerArgs struct {
15 | 	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"`
16 | 	ServiceName    string           `json:"serviceName" jsonschema:"required,description=The name of the service to get profiles for"`
17 | 	ContainerNames []string         `json:"containerNames" jsonschema:"description=The container names to get profiles for"`
18 | }
19 | 
20 | func GetProfilesHandler(ctx context.Context, arguments GetProfileHandlerArgs) (*mcpgolang.ToolResponse, error) {
21 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
22 | 	if err != nil {
23 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
24 | 	}
25 | 	request := model.GetProfileRequest{
26 | 		StartTime:      startTime,
27 | 		EndTime:        endTime,
28 | 		ServiceName:    arguments.ServiceName,
29 | 		ContainerNames: arguments.ContainerNames,
30 | 	}
31 | 
32 | 	body, err := getProfilesMetoroCall(ctx, request)
33 | 	if err != nil {
34 | 		return nil, fmt.Errorf("error getting profiles: %v", err)
35 | 	}
36 | 
37 | 	if len(body) > 200000 {
38 | 		return nil, fmt.Errorf("response too large, please refine your query to get a smaller response")
39 | 	}
40 | 
41 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
42 | }
43 | 
44 | func getProfilesMetoroCall(ctx context.Context, request model.GetProfileRequest) ([]byte, error) {
45 | 	requestBody, err := json.Marshal(request)
46 | 	if err != nil {
47 | 		return nil, fmt.Errorf("error marshaling profiles request: %v", err)
48 | 	}
49 | 	return utils.MakeMetoroAPIRequest("POST", "profiles", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
50 | }
51 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/utils"
11 | )
12 | 
13 | type ListInvestigationsHandlerArgs struct {
14 | 	Limit           int               `json:"limit,omitempty" jsonschema:"description=Maximum number of investigations to return (default 20 max 100)"`
15 | 	Offset          int               `json:"offset,omitempty" jsonschema:"description=Number of investigations to skip for pagination"`
16 | 	Tags            map[string]string `json:"tags,omitempty" jsonschema:"description=Filter investigations by tags"`
17 | 	IncludeResolved bool              `json:"includeResolved,omitempty" jsonschema:"description=Include resolved investigations in the results"`
18 | }
19 | 
20 | func ListInvestigationsHandler(ctx context.Context, arguments ListInvestigationsHandlerArgs) (*mcpgolang.ToolResponse, error) {
21 | 	// Create the request body
22 | 	request := struct {
23 | 		Limit             int               `json:"limit,omitempty"`
24 | 		Offset            int               `json:"offset,omitempty"`
25 | 		Tags              map[string]string `json:"tags,omitempty"`
26 | 		IncludeResolved   bool              `json:"includeResolved,omitempty"`
27 | 		ExcludeInProgress bool              `json:"excludeInProgress,omitempty"`
28 | 	}{
29 | 		Limit:             arguments.Limit,
30 | 		Offset:            arguments.Offset,
31 | 		Tags:              arguments.Tags,
32 | 		IncludeResolved:   arguments.IncludeResolved,
33 | 		ExcludeInProgress: true, // Always exclude in-progress investigations as the AI only wants to see the past investigations.
34 | 	}
35 | 
36 | 	requestBody, err := json.Marshal(request)
37 | 	if err != nil {
38 | 		return nil, fmt.Errorf("failed to marshal request: %w", err)
39 | 	}
40 | 
41 | 	// Make the API request
42 | 	responseBody, err := utils.MakeMetoroAPIRequest("POST", "investigations/list", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
43 | 	if err != nil {
44 | 		return nil, fmt.Errorf("failed to list investigations: %w", err)
45 | 	}
46 | 
47 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
48 | }
49 | 
```

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

```go
 1 | package utils
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"fmt"
 6 | 	"io"
 7 | 	"io/ioutil"
 8 | 	"net/http"
 9 | 	"os"
10 | 
11 | 	"github.com/gin-gonic/gin"
12 | )
13 | 
14 | const METORO_API_URL_ENV_VAR = "METORO_API_URL"
15 | const METORO_AUTH_TOKEN_ENV_VAR = "METORO_AUTH_TOKEN"
16 | 
17 | type APIRequirements struct {
18 | 	authHeader string
19 | 	metoroUrl  string
20 | }
21 | 
22 | func GetAPIRequirementsFromRequest(ctx context.Context) *APIRequirements {
23 | 	c := ctx.Value("ginContext")
24 | 	if c == nil {
25 | 		return nil
26 | 	}
27 | 	ginContext, ok := c.(*gin.Context)
28 | 	if !ok {
29 | 		return nil
30 | 	}
31 | 
32 | 	if ginContext.Request.Header.Get("Authorization") != "" {
33 | 		return &APIRequirements{
34 | 			authHeader: ginContext.Request.Header.Get("Authorization"),
35 | 			metoroUrl:  "http://localhost:8080",
36 | 		}
37 | 	}
38 | 	return nil
39 | }
40 | 
41 | // makeMetoroAPIRequest makes an HTTP request to the Metoro API with the given method, endpoint, and body.
42 | // It handles authentication and common error cases.
43 | func MakeMetoroAPIRequest(method, endpoint string, body io.Reader, apiRequirements *APIRequirements) ([]byte, error) {
44 | 	// Create a new HTTP client
45 | 	client := &http.Client{}
46 | 	if apiRequirements == nil {
47 | 		apiRequirements = &APIRequirements{
48 | 			authHeader: "Bearer " + os.Getenv(METORO_AUTH_TOKEN_ENV_VAR),
49 | 			metoroUrl:  os.Getenv(METORO_API_URL_ENV_VAR),
50 | 		}
51 | 	}
52 | 
53 | 	// Create a new request
54 | 	req, err := http.NewRequest(method, fmt.Sprintf("%s/api/v1/%s", apiRequirements.metoroUrl, endpoint), body)
55 | 	if err != nil {
56 | 		return nil, fmt.Errorf("error creating request: %v", err)
57 | 	}
58 | 
59 | 	// Add the Authorization header
60 | 	req.Header.Add("Authorization", apiRequirements.authHeader)
61 | 
62 | 	// Send the request
63 | 	resp, err := client.Do(req)
64 | 	if err != nil {
65 | 		return nil, fmt.Errorf("error sending request: %v", err)
66 | 	}
67 | 	defer resp.Body.Close()
68 | 
69 | 	// Read the response body
70 | 	responseBody, err := ioutil.ReadAll(resp.Body)
71 | 	if err != nil {
72 | 		return nil, fmt.Errorf("error reading response body: %v", err)
73 | 	}
74 | 
75 | 	// Check the response status code
76 | 	if resp.StatusCode >= 300 {
77 | 		return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(responseBody))
78 | 	}
79 | 
80 | 	return responseBody, nil
81 | }
82 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetServiceSummariesHandlerArgs struct {
15 | 	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"`
16 | 	Namespaces   string           `json:"namespace" jsonschema:"description=The namespace to get service summaries for. If empty all namespaces will be used."`
17 | 	Environments []string         `json:"environments" jsonschema:"description=The environments to get service summaries for. If empty all environments will be used."`
18 | }
19 | 
20 | func GetServiceSummariesHandler(ctx context.Context, arguments GetServiceSummariesHandlerArgs) (*mcpgolang.ToolResponse, error) {
21 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
22 | 	if err != nil {
23 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
24 | 	}
25 | 	request := model.GetServiceSummariesRequest{
26 | 		StartTime:    startTime,
27 | 		EndTime:      endTime,
28 | 		Namespace:    arguments.Namespaces,
29 | 		Environments: arguments.Environments,
30 | 	}
31 | 
32 | 	body, err := getServiceSummariesMetoroCall(ctx, request)
33 | 	if err != nil {
34 | 		return nil, fmt.Errorf("error getting service summaries: %v", err)
35 | 	}
36 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
37 | }
38 | 
39 | func getServiceSummariesMetoroCall(ctx context.Context, request model.GetServiceSummariesRequest) ([]byte, error) {
40 | 	requestBody, err := json.Marshal(request)
41 | 	if err != nil {
42 | 		return nil, fmt.Errorf("error marshaling service summaries request: %v", err)
43 | 	}
44 | 	return utils.MakeMetoroAPIRequest("POST", "serviceSummaries", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
45 | }
46 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetMetricAttributesHandlerArgs struct {
15 | 	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"`
16 | 	MetricName       string              `json:"metricName" jsonschema:"required,description=The name of the metric to get the possible attribute keys and values."`
17 | 	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]}"`
18 | }
19 | 
20 | func GetMetricAttributesHandler(ctx context.Context, arguments GetMetricAttributesHandlerArgs) (*mcpgolang.ToolResponse, error) {
21 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
22 | 	if err != nil {
23 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
24 | 	}
25 | 	request := model.MetricAttributesRequest{
26 | 		StartTime:        startTime,
27 | 		EndTime:          endTime,
28 | 		MetricName:       arguments.MetricName,
29 | 		FilterAttributes: arguments.FilterAttributes,
30 | 	}
31 | 	response, err := getMetricAttributesMetoroCall(ctx, request)
32 | 	if err != nil {
33 | 		return nil, fmt.Errorf("error calling Metoro API: %v", err)
34 | 	}
35 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(response)))), nil
36 | }
37 | 
38 | func getMetricAttributesMetoroCall(ctx context.Context, request model.MetricAttributesRequest) ([]byte, error) {
39 | 	jsonData, err := json.Marshal(request)
40 | 	if err != nil {
41 | 		return nil, err
42 | 	}
43 | 	return utils.MakeMetoroAPIRequest("POST", "metricAttributes", bytes.NewBuffer(jsonData), utils.GetAPIRequirementsFromRequest(ctx))
44 | }
45 | 
```

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

```go
  1 | /*
  2 | Metoro API
  3 | 
  4 | API for managing Metoro environments, alerts, and dashboards.
  5 | 
  6 | API version: 1.0.0
  7 | */
  8 | 
  9 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
 10 | 
 11 | package model
 12 | 
 13 | import (
 14 | 	"encoding/json"
 15 | 	"fmt"
 16 | )
 17 | 
 18 | // AlertType Type of alert
 19 | type AlertType string
 20 | 
 21 | // List of AlertType
 22 | const (
 23 | 	TIMESERIES AlertType = "timeseries"
 24 | )
 25 | 
 26 | // All allowed values of AlertType enum
 27 | var AllowedAlertTypeEnumValues = []AlertType{
 28 | 	"timeseries",
 29 | }
 30 | 
 31 | func (v *AlertType) UnmarshalJSON(src []byte) error {
 32 | 	var value string
 33 | 	err := json.Unmarshal(src, &value)
 34 | 	if err != nil {
 35 | 		return err
 36 | 	}
 37 | 	enumTypeValue := AlertType(value)
 38 | 	for _, existing := range AllowedAlertTypeEnumValues {
 39 | 		if existing == enumTypeValue {
 40 | 			*v = enumTypeValue
 41 | 			return nil
 42 | 		}
 43 | 	}
 44 | 
 45 | 	return fmt.Errorf("%+v is not a valid AlertType", value)
 46 | }
 47 | 
 48 | // NewAlertTypeFromValue returns a pointer to a valid AlertType
 49 | // for the value passed as argument, or an error if the value passed is not allowed by the enum
 50 | func NewAlertTypeFromValue(v string) (*AlertType, error) {
 51 | 	ev := AlertType(v)
 52 | 	if ev.IsValid() {
 53 | 		return &ev, nil
 54 | 	} else {
 55 | 		return nil, fmt.Errorf("invalid value '%v' for AlertType: valid values are %v", v, AllowedAlertTypeEnumValues)
 56 | 	}
 57 | }
 58 | 
 59 | // IsValid return true if the value is valid for the enum, false otherwise
 60 | func (v AlertType) IsValid() bool {
 61 | 	for _, existing := range AllowedAlertTypeEnumValues {
 62 | 		if existing == v {
 63 | 			return true
 64 | 		}
 65 | 	}
 66 | 	return false
 67 | }
 68 | 
 69 | // Ptr returns reference to AlertType value
 70 | func (v AlertType) Ptr() *AlertType {
 71 | 	return &v
 72 | }
 73 | 
 74 | type NullableAlertType struct {
 75 | 	value *AlertType
 76 | 	isSet bool
 77 | }
 78 | 
 79 | func (v NullableAlertType) Get() *AlertType {
 80 | 	return v.value
 81 | }
 82 | 
 83 | func (v *NullableAlertType) Set(val *AlertType) {
 84 | 	v.value = val
 85 | 	v.isSet = true
 86 | }
 87 | 
 88 | func (v NullableAlertType) IsSet() bool {
 89 | 	return v.isSet
 90 | }
 91 | 
 92 | func (v *NullableAlertType) Unset() {
 93 | 	v.value = nil
 94 | 	v.isSet = false
 95 | }
 96 | 
 97 | func NewNullableAlertType(val *AlertType) *NullableAlertType {
 98 | 	return &NullableAlertType{value: val, isSet: true}
 99 | }
100 | 
101 | func (v NullableAlertType) MarshalJSON() ([]byte, error) {
102 | 	return json.Marshal(v.value)
103 | }
104 | 
105 | func (v *NullableAlertType) UnmarshalJSON(src []byte) error {
106 | 	v.isSet = true
107 | 	return json.Unmarshal(src, &v.value)
108 | }
109 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/utils"
11 | )
12 | 
13 | type GetResourcesByIpHandlerArgs struct {
14 | 	Ip          string           `json:"ip" jsonschema:"required,description=IP address to search for"`
15 | 	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"`
16 | 	Environment string           `json:"environment" jsonschema:"required,description=Environment to filter the resources by"`
17 | }
18 | 
19 | type GetResourcesByIpRequest struct {
20 | 	Ip          string `json:"ip"`
21 | 	StartTime   int64  `json:"startTime"`
22 | 	EndTime     int64  `json:"endTime"`
23 | 	Environment string `json:"environment"`
24 | }
25 | 
26 | type GetResourcesByIpResponse struct {
27 | 	Resources []ResourcesByIpData `json:"resources"`
28 | }
29 | 
30 | type ResourcesByIpData struct {
31 | 	Name        string `json:"name"`
32 | 	Namespace   string `json:"namespace"`
33 | 	Ip          string `json:"ip"`
34 | 	NodeName    string `json:"nodeName"`
35 | 	Status      string `json:"status"`
36 | 	Environment string `json:"environment"`
37 | }
38 | 
39 | func GetResourcesByIpHandler(ctx context.Context, arguments GetResourcesByIpHandlerArgs) (*mcpgolang.ToolResponse, error) {
40 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
41 | 	if err != nil {
42 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
43 | 	}
44 | 
45 | 	request := GetResourcesByIpRequest{
46 | 		Ip:          arguments.Ip,
47 | 		StartTime:   startTime,
48 | 		EndTime:     endTime,
49 | 		Environment: arguments.Environment,
50 | 	}
51 | 
52 | 	resp, err := getPodByIpMetoroCall(ctx, request)
53 | 	if err != nil {
54 | 		return nil, fmt.Errorf("error getting pod by IP: %v", err)
55 | 	}
56 | 
57 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
58 | }
59 | 
60 | func getPodByIpMetoroCall(ctx context.Context, request GetResourcesByIpRequest) ([]byte, error) {
61 | 	requestBody, err := json.Marshal(request)
62 | 	if err != nil {
63 | 		return nil, fmt.Errorf("error marshaling pod by IP request: %v", err)
64 | 	}
65 | 	return utils.MakeMetoroAPIRequest("POST", "k8s/resources/byIp", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
66 | }
67 | 
```

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

```go
  1 | /*
  2 | Metoro API
  3 | 
  4 | API for managing Metoro environments, alerts, and dashboards.
  5 | 
  6 | API version: 1.0.0
  7 | */
  8 | 
  9 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
 10 | 
 11 | package model
 12 | 
 13 | import (
 14 | 	"encoding/json"
 15 | 	"fmt"
 16 | )
 17 | 
 18 | // ConditionType Type of alert condition
 19 | type ConditionType string
 20 | 
 21 | // List of ConditionType
 22 | const (
 23 | 	STATIC ConditionType = "static"
 24 | )
 25 | 
 26 | // All allowed values of ConditionType enum
 27 | var AllowedConditionTypeEnumValues = []ConditionType{
 28 | 	"static",
 29 | }
 30 | 
 31 | func (v *ConditionType) UnmarshalJSON(src []byte) error {
 32 | 	var value string
 33 | 	err := json.Unmarshal(src, &value)
 34 | 	if err != nil {
 35 | 		return err
 36 | 	}
 37 | 	enumTypeValue := ConditionType(value)
 38 | 	for _, existing := range AllowedConditionTypeEnumValues {
 39 | 		if existing == enumTypeValue {
 40 | 			*v = enumTypeValue
 41 | 			return nil
 42 | 		}
 43 | 	}
 44 | 
 45 | 	return fmt.Errorf("%+v is not a valid ConditionType", value)
 46 | }
 47 | 
 48 | // NewConditionTypeFromValue returns a pointer to a valid ConditionType
 49 | // for the value passed as argument, or an error if the value passed is not allowed by the enum
 50 | func NewConditionTypeFromValue(v string) (*ConditionType, error) {
 51 | 	ev := ConditionType(v)
 52 | 	if ev.IsValid() {
 53 | 		return &ev, nil
 54 | 	} else {
 55 | 		return nil, fmt.Errorf("invalid value '%v' for ConditionType: valid values are %v", v, AllowedConditionTypeEnumValues)
 56 | 	}
 57 | }
 58 | 
 59 | // IsValid return true if the value is valid for the enum, false otherwise
 60 | func (v ConditionType) IsValid() bool {
 61 | 	for _, existing := range AllowedConditionTypeEnumValues {
 62 | 		if existing == v {
 63 | 			return true
 64 | 		}
 65 | 	}
 66 | 	return false
 67 | }
 68 | 
 69 | // Ptr returns reference to ConditionType value
 70 | func (v ConditionType) Ptr() *ConditionType {
 71 | 	return &v
 72 | }
 73 | 
 74 | type NullableConditionType struct {
 75 | 	value *ConditionType
 76 | 	isSet bool
 77 | }
 78 | 
 79 | func (v NullableConditionType) Get() *ConditionType {
 80 | 	return v.value
 81 | }
 82 | 
 83 | func (v *NullableConditionType) Set(val *ConditionType) {
 84 | 	v.value = val
 85 | 	v.isSet = true
 86 | }
 87 | 
 88 | func (v NullableConditionType) IsSet() bool {
 89 | 	return v.isSet
 90 | }
 91 | 
 92 | func (v *NullableConditionType) Unset() {
 93 | 	v.value = nil
 94 | 	v.isSet = false
 95 | }
 96 | 
 97 | func NewNullableConditionType(val *ConditionType) *NullableConditionType {
 98 | 	return &NullableConditionType{value: val, isSet: true}
 99 | }
100 | 
101 | func (v NullableConditionType) MarshalJSON() ([]byte, error) {
102 | 	return json.Marshal(v.value)
103 | }
104 | 
105 | func (v *NullableConditionType) UnmarshalJSON(src []byte) error {
106 | 	v.isSet = true
107 | 	return json.Unmarshal(src, &v.value)
108 | }
109 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetPodsHandlerArgs struct {
15 | 	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"`
16 | 	ServiceName  string           `json:"serviceName" jsonschema:"description=The name of the service to get pods for. One of serviceName or nodeName is required"`
17 | 	NodeName     string           `json:"nodeName" jsonschema:"description=The name of the node to get pods for. One of serviceName or nodeName is required"`
18 | 	Environments []string         `json:"environments" jsonschema:"description=The environments to get pods for. If empty, all environments will be used."`
19 | }
20 | 
21 | func GetPodsHandler(ctx context.Context, arguments GetPodsHandlerArgs) (*mcpgolang.ToolResponse, error) {
22 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
23 | 	if err != nil {
24 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
25 | 	}
26 | 
27 | 	// One of serviceName or nodeName is required.
28 | 	if arguments.ServiceName == "" && arguments.NodeName == "" {
29 | 		return nil, fmt.Errorf("one of serviceName or nodeName is required")
30 | 	}
31 | 
32 | 	request := model.GetPodsRequest{
33 | 		StartTime:    startTime,
34 | 		EndTime:      endTime,
35 | 		Environments: arguments.Environments,
36 | 		ServiceName:  arguments.ServiceName,
37 | 		NodeName:     arguments.NodeName,
38 | 	}
39 | 
40 | 	jsonBody, err := json.Marshal(request)
41 | 	if err != nil {
42 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
43 | 	}
44 | 
45 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "k8s/pods", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
46 | 	if err != nil {
47 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
48 | 	}
49 | 
50 | 	response := []PodsResponse{}
51 | 	err = json.Unmarshal(resp, &response)
52 | 	if err != nil {
53 | 		return nil, fmt.Errorf("error unmarshaling response: %v", err)
54 | 	}
55 | 
56 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprint(response))), nil
57 | }
58 | 
59 | type PodsResponse struct {
60 | 	Name        string `json:"name"`
61 | 	Environment string `json:"environment"`
62 | 	Status      string `json:"status"`
63 | }
64 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetNodesHandlerArgs struct {
15 | 	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"`
16 | 	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."`
17 | 	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."`
18 | 	Environments   []string            `json:"environments" jsonschema:"description=The environments to get nodes that belong to. If empty all environments will be used."`
19 | }
20 | 
21 | func GetNodesHandler(ctx context.Context, arguments GetNodesHandlerArgs) (*mcpgolang.ToolResponse, error) {
22 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
23 | 	if err != nil {
24 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
25 | 	}
26 | 	request := model.GetAllNodesRequest{
27 | 		StartTime:      startTime,
28 | 		EndTime:        endTime,
29 | 		Filters:        arguments.Filters,
30 | 		ExcludeFilters: arguments.ExcludeFilters,
31 | 		Splits:         []string{},
32 | 		Environments:   arguments.Environments,
33 | 	}
34 | 	body, err := getNodesMetoroCall(ctx, request)
35 | 	if err != nil {
36 | 		return nil, fmt.Errorf("error getting nodes: %v", err)
37 | 	}
38 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
39 | }
40 | 
41 | func getNodesMetoroCall(ctx context.Context, request model.GetAllNodesRequest) ([]byte, error) {
42 | 	requestBody, err := json.Marshal(request)
43 | 	if err != nil {
44 | 		return nil, fmt.Errorf("error marshaling nodes request: %v", err)
45 | 	}
46 | 	return utils.MakeMetoroAPIRequest("POST", "infrastructure/nodes", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
47 | }
48 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/utils"
11 | )
12 | 
13 | type GetSourceRepositoryHandlerArgs struct {
14 | 	// Required: Service name to get the source repository for
15 | 	ServiceName string `json:"serviceName" jsonschema:"required,description=The name of the service to get the source repository for"`
16 | 
17 | 	// Optional: Environment to filter by. If not provided, all environments are considered
18 | 	Environments []string `json:"environments" jsonschema:"description=List of environments to search for the service in. If empty all environments will be considered"`
19 | 
20 | 	// Required: Time configuration for the query
21 | 	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."`
22 | }
23 | 
24 | type GetSourceRepositoryRequest struct {
25 | 	ServiceName  string   `json:"serviceName"`
26 | 	Environments []string `json:"environments"`
27 | 	StartTime    int64    `json:"startTime"`
28 | 	EndTime      int64    `json:"endTime"`
29 | }
30 | 
31 | type GetSourceRepositoryResponse struct {
32 | 	// The source repository URL/path found in the deployment
33 | 	Repository string `json:"repository"`
34 | 
35 | 	// Whether a repository was found
36 | 	Found bool `json:"found"`
37 | 
38 | 	// The environment where the repository information was found
39 | 	Environment string `json:"environment,omitempty"`
40 | }
41 | 
42 | func GetSourceRepositoryHandler(ctx context.Context, arguments GetSourceRepositoryHandlerArgs) (*mcpgolang.ToolResponse, error) {
43 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
44 | 	if err != nil {
45 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
46 | 	}
47 | 
48 | 	body, err := getSourceRepositoryMetoroCall(ctx, arguments, startTime, endTime)
49 | 	if err != nil {
50 | 		return nil, fmt.Errorf("error getting source repository: %v", err)
51 | 	}
52 | 
53 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
54 | }
55 | 
56 | func getSourceRepositoryMetoroCall(ctx context.Context, args GetSourceRepositoryHandlerArgs, startTime, endTime int64) ([]byte, error) {
57 | 	req := GetSourceRepositoryRequest{
58 | 		ServiceName:  args.ServiceName,
59 | 		Environments: args.Environments,
60 | 		StartTime:    startTime,
61 | 		EndTime:      endTime,
62 | 	}
63 | 
64 | 	reqBody, err := json.Marshal(req)
65 | 	if err != nil {
66 | 		return nil, fmt.Errorf("error marshalling request: %v", err)
67 | 	}
68 | 
69 | 	return utils.MakeMetoroAPIRequest("POST", "source/repository", bytes.NewBuffer(reqBody), utils.GetAPIRequirementsFromRequest(ctx))
70 | }
71 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/utils"
11 | )
12 | 
13 | type GetTraceSpansHandlerArgs struct {
14 | 	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"`
15 | 	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."`
16 | 	Environments []string         `json:"environments" jsonschema:"description=The environments to get the spans for. If empty all environments will be included"`
17 | }
18 | 
19 | type GetSpansForTraceRequest struct {
20 | 	// Required: Start time of when to get the traces in seconds since epoch
21 | 	StartTime int64 `json:"startTime"`
22 | 	// Required: End time of when to get the traces in seconds since epoch
23 | 	EndTime int64 `json:"endTime"`
24 | 	// Required: The traceId of the trace to get the associated spans.
25 | 	TraceId string `json:"traceId"`
26 | 	// The environments to get the traces for. If empty, all environments will be included
27 | 	Environments                   []string `json:"environments"`
28 | 	ShouldReturnNonMetoroEpbfSpans bool     `json:"shouldReturnNonMetoroEpbfSpans"`
29 | }
30 | 
31 | func GetTraceSpansHandler(ctx context.Context, arguments GetTraceSpansHandlerArgs) (*mcpgolang.ToolResponse, error) {
32 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
33 | 	if err != nil {
34 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
35 | 	}
36 | 
37 | 	request := GetSpansForTraceRequest{
38 | 		StartTime:                      startTime,
39 | 		EndTime:                        endTime,
40 | 		TraceId:                        arguments.TraceId,
41 | 		Environments:                   arguments.Environments,
42 | 		ShouldReturnNonMetoroEpbfSpans: true,
43 | 	}
44 | 
45 | 	body, err := getTraceSpansMetoroCall(ctx, request)
46 | 	if err != nil {
47 | 		return nil, fmt.Errorf("error getting trace spans: %v", err)
48 | 	}
49 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
50 | }
51 | 
52 | func getTraceSpansMetoroCall(ctx context.Context, request GetSpansForTraceRequest) ([]byte, error) {
53 | 	requestBody, err := json.Marshal(request)
54 | 	if err != nil {
55 | 		return nil, fmt.Errorf("error marshaling trace spans request: %v", err)
56 | 	}
57 | 	return utils.MakeMetoroAPIRequest("POST", "spans", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
58 | }
59 | 
```

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

```go
  1 | /*
  2 | Metoro API
  3 | 
  4 | API for managing Metoro environments, alerts, and dashboards.
  5 | 
  6 | API version: 1.0.0
  7 | */
  8 | 
  9 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
 10 | 
 11 | package model
 12 | 
 13 | import (
 14 | 	"encoding/json"
 15 | 	"fmt"
 16 | )
 17 | 
 18 | // OperatorType Type of comparison operator
 19 | type OperatorType string
 20 | 
 21 | // List of OperatorType
 22 | const (
 23 | 	GREATER_THAN          OperatorType = "greaterThan"
 24 | 	LESS_THAN             OperatorType = "lessThan"
 25 | 	GREATER_THAN_OR_EQUAL OperatorType = "greaterThanOrEqual"
 26 | 	LESS_THAN_OR_EQUAL    OperatorType = "lessThanOrEqual"
 27 | 	EQUALS                OperatorType = "equals"
 28 | 	NOT_EQUALS            OperatorType = "notEquals"
 29 | )
 30 | 
 31 | // All allowed values of OperatorType enum
 32 | var AllowedOperatorTypeEnumValues = []OperatorType{
 33 | 	"greaterThan",
 34 | 	"lessThan",
 35 | 	"greaterThanOrEqual",
 36 | 	"lessThanOrEqual",
 37 | 	"equals",
 38 | 	"notEquals",
 39 | }
 40 | 
 41 | func (v *OperatorType) UnmarshalJSON(src []byte) error {
 42 | 	var value string
 43 | 	err := json.Unmarshal(src, &value)
 44 | 	if err != nil {
 45 | 		return err
 46 | 	}
 47 | 	enumTypeValue := OperatorType(value)
 48 | 	for _, existing := range AllowedOperatorTypeEnumValues {
 49 | 		if existing == enumTypeValue {
 50 | 			*v = enumTypeValue
 51 | 			return nil
 52 | 		}
 53 | 	}
 54 | 
 55 | 	return fmt.Errorf("%+v is not a valid OperatorType", value)
 56 | }
 57 | 
 58 | // NewOperatorTypeFromValue returns a pointer to a valid OperatorType
 59 | // for the value passed as argument, or an error if the value passed is not allowed by the enum
 60 | func NewOperatorTypeFromValue(v string) (*OperatorType, error) {
 61 | 	ev := OperatorType(v)
 62 | 	if ev.IsValid() {
 63 | 		return &ev, nil
 64 | 	} else {
 65 | 		return nil, fmt.Errorf("invalid value '%v' for OperatorType: valid values are %v", v, AllowedOperatorTypeEnumValues)
 66 | 	}
 67 | }
 68 | 
 69 | // IsValid return true if the value is valid for the enum, false otherwise
 70 | func (v OperatorType) IsValid() bool {
 71 | 	for _, existing := range AllowedOperatorTypeEnumValues {
 72 | 		if existing == v {
 73 | 			return true
 74 | 		}
 75 | 	}
 76 | 	return false
 77 | }
 78 | 
 79 | // Ptr returns reference to OperatorType value
 80 | func (v OperatorType) Ptr() *OperatorType {
 81 | 	return &v
 82 | }
 83 | 
 84 | type NullableOperatorType struct {
 85 | 	value *OperatorType
 86 | 	isSet bool
 87 | }
 88 | 
 89 | func (v NullableOperatorType) Get() *OperatorType {
 90 | 	return v.value
 91 | }
 92 | 
 93 | func (v *NullableOperatorType) Set(val *OperatorType) {
 94 | 	v.value = val
 95 | 	v.isSet = true
 96 | }
 97 | 
 98 | func (v NullableOperatorType) IsSet() bool {
 99 | 	return v.isSet
100 | }
101 | 
102 | func (v *NullableOperatorType) Unset() {
103 | 	v.value = nil
104 | 	v.isSet = false
105 | }
106 | 
107 | func NewNullableOperatorType(val *OperatorType) *NullableOperatorType {
108 | 	return &NullableOperatorType{value: val, isSet: true}
109 | }
110 | 
111 | func (v NullableOperatorType) MarshalJSON() ([]byte, error) {
112 | 	return json.Marshal(v.value)
113 | }
114 | 
115 | func (v *NullableOperatorType) UnmarshalJSON(src []byte) error {
116 | 	v.isSet = true
117 | 	return json.Unmarshal(src, &v.value)
118 | }
119 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetK8sEventsVolumeHandlerArgs struct {
15 | 	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"`
16 | 	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)"`
17 | 	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)"`
18 | 	Regexes        []string            `json:"regexes" jsonschema:"description=Only the events with messages that match these regexes will be counted"`
19 | 	ExcludeRegexes []string            `json:"excludeRegexes" jsonschema:"description=Events with messages that match these regexes will not be counted"`
20 | 	Environments   []string            `json:"environments" jsonschema:"description=Environments to get events from"`
21 | }
22 | 
23 | func GetK8sEventsVolumeHandler(ctx context.Context, arguments GetK8sEventsVolumeHandlerArgs) (*mcpgolang.ToolResponse, error) {
24 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
25 | 	if err != nil {
26 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
27 | 	}
28 | 	request := model.GetK8sEventMetricsRequest{
29 | 		StartTime:      startTime,
30 | 		EndTime:        endTime,
31 | 		Filters:        arguments.Filters,
32 | 		ExcludeFilters: arguments.ExcludeFilters,
33 | 		Regexes:        arguments.Regexes,
34 | 		ExcludeRegexes: arguments.ExcludeRegexes,
35 | 		Environments:   arguments.Environments,
36 | 		Splits:         []string{"EventType"}, // We want the volume to be split by EventType so we can see the breakdown of Warning/Normal events.
37 | 	}
38 | 
39 | 	jsonBody, err := json.Marshal(request)
40 | 	if err != nil {
41 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
42 | 	}
43 | 
44 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "k8s/events/metrics", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
45 | 	if err != nil {
46 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
47 | 	}
48 | 
49 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
50 | }
51 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetTracesDistributionHandlerArgs struct {
15 | 	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."`
16 | 	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"`
17 | 	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"`
18 | }
19 | 
20 | func GetTracesDistributionHandler(ctx context.Context, arguments GetTracesDistributionHandlerArgs) (*mcpgolang.ToolResponse, error) {
21 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
22 | 	if err != nil {
23 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
24 | 	}
25 | 
26 | 	err = CheckAttributes(ctx, model.Trace, arguments.Filters, arguments.ExcludeFilters, []string{}, nil)
27 | 	if err != nil {
28 | 		return nil, err
29 | 	}
30 | 
31 | 	limit := 20
32 | 
33 | 	request := model.GetTracesRequest{
34 | 		StartTime:      startTime,
35 | 		EndTime:        endTime,
36 | 		Filters:        arguments.Filters,
37 | 		ExcludeFilters: arguments.ExcludeFilters,
38 | 		Limit:          &limit,
39 | 	}
40 | 
41 | 	body, err := getTracesDistributionMetoroCall(ctx, request)
42 | 	if err != nil {
43 | 		return nil, fmt.Errorf("error getting traces distribution: %v", err)
44 | 	}
45 | 
46 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
47 | }
48 | 
49 | func getTracesDistributionMetoroCall(ctx context.Context, request model.GetTracesRequest) ([]byte, error) {
50 | 	requestBody, err := json.Marshal(request)
51 | 	if err != nil {
52 | 		return nil, fmt.Errorf("error marshaling traces distribution request: %v", err)
53 | 	}
54 | 	return utils.MakeMetoroAPIRequest("POST", "traces/distribution", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
55 | }
```

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

```go
 1 | package resources
 2 | 
 3 | type MetoroResource struct {
 4 | 	Path        string
 5 | 	Name        string
 6 | 	Description string
 7 | 	ContentType string
 8 | 	Handler     any
 9 | }
10 | 
11 | var MetoroResourcesList = []MetoroResource{
12 | 	{
13 | 		Path:        "api://environments",
14 | 		Name:        "environments",
15 | 		Description: "This resource provides a list of names of the kubernetes clusters/environments monitored by Metoro",
16 | 		ContentType: "text/plain",
17 | 		Handler:     EnvironmentResourceHandler,
18 | 	},
19 | 	{
20 | 		Path:        "api://namespaces",
21 | 		Name:        "namespaces",
22 | 		Description: "This resource provides a list of namespaces in the kubernetes clusters/environments monitored by Metoro",
23 | 		ContentType: "text/plain",
24 | 		Handler:     NamespacesResourceHandler,
25 | 	},
26 | 	{
27 | 		Path:        "api://services",
28 | 		Name:        "services",
29 | 		Description: "This resource provides a list of services running in the kubernetes clusters/environments monitored by Metoro",
30 | 		ContentType: "text/plain",
31 | 		Handler:     ServicesResourceHandler,
32 | 	},
33 | 	{
34 | 		Path:        "api://traceAttributes",
35 | 		Name:        "traceAttributes",
36 | 		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.",
37 | 		ContentType: "text/plain",
38 | 		Handler:     TraceAttributesResourceHandler,
39 | 	},
40 | 	{
41 | 		Path:        "api://k8sEventAttributes",
42 | 		Name:        "k8sEventAttributes",
43 | 		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.",
44 | 		ContentType: "text/plain",
45 | 		Handler:     K8sEventsAttributesResourceHandler,
46 | 	},
47 | 	{
48 | 		Path:        "api://metrics",
49 | 		Name:        "metricNames",
50 | 		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.",
51 | 		ContentType: "text/plain",
52 | 		Handler:     MetricsResourceHandler,
53 | 	},
54 | 	{
55 | 		Path:        "api://logAttributes",
56 | 		Name:        "logAttributes",
57 | 		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.",
58 | 		ContentType: "text/plain",
59 | 		Handler:     LogAttributesResourceHandler,
60 | 	},
61 | 	{
62 | 		Path:        "api://nodes",
63 | 		Name:        "nodes",
64 | 		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.",
65 | 		ContentType: "text/plain",
66 | 		Handler:     NodesResourceHandler,
67 | 	},
68 | }
69 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetK8sEventsHandlerArgs struct {
15 | 	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"`
16 | 	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)"`
17 | 	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)"`
18 | 	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']"`
19 | 	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']"`
20 | 	Environments   []string            `json:"environments" jsonschema:"description=Environments/Clusters to get events for"`
21 | }
22 | 
23 | func GetK8sEventsHandler(ctx context.Context, arguments GetK8sEventsHandlerArgs) (*mcpgolang.ToolResponse, error) {
24 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
25 | 	if err != nil {
26 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
27 | 	}
28 | 	request := model.GetK8sEventsRequest{
29 | 		StartTime:      startTime,
30 | 		EndTime:        endTime,
31 | 		Filters:        arguments.Filters,
32 | 		ExcludeFilters: arguments.ExcludeFilters,
33 | 		Regexes:        arguments.Regexes,
34 | 		ExcludeRegexes: arguments.ExcludeRegexes,
35 | 		Environments:   arguments.Environments,
36 | 	}
37 | 
38 | 	jsonBody, err := json.Marshal(request)
39 | 	if err != nil {
40 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
41 | 	}
42 | 
43 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "k8s/events", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
44 | 	if err != nil {
45 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
46 | 	}
47 | 
48 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
49 | }
50 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 9 | 	"github.com/metoro-io/metoro-mcp-server/model"
10 | 	"github.com/metoro-io/metoro-mcp-server/utils"
11 | 	"slices"
12 | 	"strings"
13 | 	"time"
14 | )
15 | 
16 | type GetAttributeKeysHandlerArgs struct {
17 | 	Type       model.MetricType `json:"type" jsonschema:"required,description=The type of attribute keys to get. Either 'logs' or 'trace' or 'metric' or 'kubernetes_resource'"`
18 | 	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"`
19 | 	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'"`
20 | }
21 | 
22 | func GetAttributeKeysHandler(ctx context.Context, arguments GetAttributeKeysHandlerArgs) (*mcpgolang.ToolResponse, error) {
23 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
24 | 	if err != nil {
25 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
26 | 	}
27 | 
28 | 	if arguments.Type == model.Metric {
29 | 		if arguments.MetricName == "" {
30 | 			return nil, fmt.Errorf("metricName is required when type is 'metric'")
31 | 		}
32 | 
33 | 		// Check whether the metric is valid. If not, return an error.
34 | 		err = CheckMetric(ctx, arguments.MetricName)
35 | 		if err != nil {
36 | 			return nil, err
37 | 		}
38 | 	}
39 | 
40 | 	metricAttr := model.GetMetricAttributesRequest{
41 | 		StartTime:    startTime,
42 | 		EndTime:      endTime,
43 | 		MetricName:   arguments.MetricName,
44 | 		Environments: []string{}, // TODO: Add environments to the request if needed. For now, we are not using it as I don't think its needed.
45 | 	}
46 | 
47 | 	request := model.MultiMetricAttributeKeysRequest{
48 | 		Type:   string(arguments.Type),
49 | 		Metric: &metricAttr,
50 | 	}
51 | 	jsonBody, err := json.Marshal(request)
52 | 	if err != nil {
53 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
54 | 	}
55 | 
56 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "metrics/attributes", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
57 | 
58 | 	if err != nil {
59 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
60 | 	}
61 | 
62 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
63 | }
64 | 
65 | func CheckMetric(ctx context.Context, metricName string) error {
66 | 	now := time.Now()
67 | 	hourAgo := now.Add(-30 * time.Minute)
68 | 	request := model.FuzzyMetricsRequest{
69 | 		StartTime:        hourAgo.Unix(),
70 | 		EndTime:          now.Unix(),
71 | 		MetricFuzzyMatch: "", // This will return all the metric names.
72 | 	}
73 | 	metricNamesResp, err := getMetricNamesMetoroCall(ctx, request)
74 | 
75 | 	metricNames := model.GetMetricNamesResponse{}
76 | 	err = json.Unmarshal(metricNamesResp, &metricNames)
77 | 	if err != nil {
78 | 		return fmt.Errorf("error unmarshaling response: %v", err)
79 | 	}
80 | 
81 | 	metricNamesStr := strings.Join(metricNames.MetricNames, ", ")
82 | 	if !slices.Contains(metricNames.MetricNames, metricName) {
83 | 		return fmt.Errorf("metricName '%s' is not valid. Valid metric names are: %s", metricName, metricNamesStr)
84 | 	}
85 | 
86 | 	return nil
87 | }
88 | 
```

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

```go
  1 | /*
  2 | Metoro API
  3 | 
  4 | API for managing Metoro environments, alerts, and dashboards.
  5 | 
  6 | API version: 1.0.0
  7 | */
  8 | 
  9 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
 10 | 
 11 | package model
 12 | 
 13 | import (
 14 | 	"encoding/json"
 15 | )
 16 | 
 17 | // checks if the ExpressionConfig type satisfies the MappedNullable interface at compile time
 18 | var _ MappedNullable = &ExpressionConfig{}
 19 | 
 20 | // ExpressionConfig struct for ExpressionConfig
 21 | type ExpressionConfig struct {
 22 | 	MetoroQLTimeseries *MetoroQlTimeseries `json:"metoroQLTimeseries,omitempty"`
 23 | }
 24 | 
 25 | // NewExpressionConfig instantiates a new ExpressionConfig object
 26 | // This constructor will assign default values to properties that have it defined,
 27 | // and makes sure properties required by API are set, but the set of arguments
 28 | // will change when the set of required properties is changed
 29 | func NewExpressionConfig() *ExpressionConfig {
 30 | 	this := ExpressionConfig{}
 31 | 	return &this
 32 | }
 33 | 
 34 | // NewExpressionConfigWithDefaults instantiates a new ExpressionConfig object
 35 | // This constructor will only assign default values to properties that have it defined,
 36 | // but it doesn't guarantee that properties required by API are set
 37 | func NewExpressionConfigWithDefaults() *ExpressionConfig {
 38 | 	this := ExpressionConfig{}
 39 | 	return &this
 40 | }
 41 | 
 42 | // GetMetoroQLTimeseries returns the MetoroQLTimeseries field value if set, zero value otherwise.
 43 | func (o *ExpressionConfig) GetMetoroQLTimeseries() MetoroQlTimeseries {
 44 | 	if o == nil || IsNil(o.MetoroQLTimeseries) {
 45 | 		var ret MetoroQlTimeseries
 46 | 		return ret
 47 | 	}
 48 | 	return *o.MetoroQLTimeseries
 49 | }
 50 | 
 51 | // GetMetoroQLTimeseriesOk returns a tuple with the MetoroQLTimeseries field value if set, nil otherwise
 52 | // and a boolean to check if the value has been set.
 53 | func (o *ExpressionConfig) GetMetoroQLTimeseriesOk() (*MetoroQlTimeseries, bool) {
 54 | 	if o == nil || IsNil(o.MetoroQLTimeseries) {
 55 | 		return nil, false
 56 | 	}
 57 | 	return o.MetoroQLTimeseries, true
 58 | }
 59 | 
 60 | // HasMetoroQLTimeseries returns a boolean if a field has been set.
 61 | func (o *ExpressionConfig) HasMetoroQLTimeseries() bool {
 62 | 	if o != nil && !IsNil(o.MetoroQLTimeseries) {
 63 | 		return true
 64 | 	}
 65 | 
 66 | 	return false
 67 | }
 68 | 
 69 | // SetMetoroQLTimeseries gets a reference to the given MetoroQlTimeseries and assigns it to the MetoroQLTimeseries field.
 70 | func (o *ExpressionConfig) SetMetoroQLTimeseries(v MetoroQlTimeseries) {
 71 | 	o.MetoroQLTimeseries = &v
 72 | }
 73 | 
 74 | func (o ExpressionConfig) MarshalJSON() ([]byte, error) {
 75 | 	toSerialize, err := o.ToMap()
 76 | 	if err != nil {
 77 | 		return []byte{}, err
 78 | 	}
 79 | 	return json.Marshal(toSerialize)
 80 | }
 81 | 
 82 | func (o ExpressionConfig) ToMap() (map[string]interface{}, error) {
 83 | 	toSerialize := map[string]interface{}{}
 84 | 	if !IsNil(o.MetoroQLTimeseries) {
 85 | 		toSerialize["metoroQLTimeseries"] = o.MetoroQLTimeseries
 86 | 	}
 87 | 	return toSerialize, nil
 88 | }
 89 | 
 90 | type NullableExpressionConfig struct {
 91 | 	value *ExpressionConfig
 92 | 	isSet bool
 93 | }
 94 | 
 95 | func (v NullableExpressionConfig) Get() *ExpressionConfig {
 96 | 	return v.value
 97 | }
 98 | 
 99 | func (v *NullableExpressionConfig) Set(val *ExpressionConfig) {
100 | 	v.value = val
101 | 	v.isSet = true
102 | }
103 | 
104 | func (v NullableExpressionConfig) IsSet() bool {
105 | 	return v.isSet
106 | }
107 | 
108 | func (v *NullableExpressionConfig) Unset() {
109 | 	v.value = nil
110 | 	v.isSet = false
111 | }
112 | 
113 | func NewNullableExpressionConfig(val *ExpressionConfig) *NullableExpressionConfig {
114 | 	return &NullableExpressionConfig{value: val, isSet: true}
115 | }
116 | 
117 | func (v NullableExpressionConfig) MarshalJSON() ([]byte, error) {
118 | 	return json.Marshal(v.value)
119 | }
120 | 
121 | func (v *NullableExpressionConfig) UnmarshalJSON(src []byte) error {
122 | 	v.isSet = true
123 | 	return json.Unmarshal(src, &v.value)
124 | }
125 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/utils"
11 | )
12 | 
13 | type GetServiceGraphHandlerArgs struct {
14 | 	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"`
15 | 	ServiceName  string           `json:"serviceName" jsonschema:"required,description=The name of the service to get the graph for"`
16 | 	Environments []string         `json:"environments" jsonschema:"description=The environments to get the service graph for. If empty all environments will be used."`
17 | }
18 | 
19 | type GetServiceGraphRequest struct {
20 | 	// If Environments is not empty, only services that are in the list will be included in the graph.
21 | 	// If Environments is empty, all services will be included in the graph.
22 | 	Environments []string `json:"environments"`
23 | 	// If InitialServices is not empty, only services that are in the list will be included in the graph.
24 | 	// If InitialServices is empty, all services will be included in the graph.
25 | 	InitialServices []string `json:"initialServices"`
26 | 	// If EndingServices is not empty, only services that are in the list will be included in the graph.
27 | 	// If EndingServices is empty, all services will be included in the graph.
28 | 	EndingServices []string `json:"endingServices"`
29 | 	// StartTime is the start time of the graph in seconds since epoch
30 | 	StartTime int64 `json:"startTime"`
31 | 	// EndTime is the end time of the graph in seconds since epoch
32 | 	EndTime int64 `json:"endTime"`
33 | 	// The filters to apply to the traces, so for example, if you want to get traces for a specific service
34 | 	// you can pass in a filter like {"service_name": ["microservice_a"]}
35 | 	Filters map[string][]string `json:"filters"`
36 | 	// ExcludeFilters are filters that should be excluded from the traces
37 | 	// For example, if you want to get traces for all services except microservice_a you can pass in
38 | 	// {"service_name": ["microservice_a"]}
39 | 	ExcludeFilters map[string][]string `json:"excludeFilters"`
40 | 	// Regexes are used to filter traces based on a regex inclusively
41 | 	Regexes []string `json:"regexes"`
42 | 	// ExcludeRegexes are used to filter traces based on a regex exclusively
43 | 	ExcludeRegexes []string `json:"excludeRegexes"`
44 | }
45 | 
46 | func GetServiceGraphHandler(ctx context.Context, arguments GetServiceGraphHandlerArgs) (*mcpgolang.ToolResponse, error) {
47 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
48 | 	if err != nil {
49 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
50 | 	}
51 | 
52 | 	request := GetServiceGraphRequest{
53 | 		StartTime:       startTime,
54 | 		EndTime:         endTime,
55 | 		Environments:    arguments.Environments,
56 | 		InitialServices: []string{arguments.ServiceName},
57 | 		EndingServices:  []string{arguments.ServiceName},
58 | 	}
59 | 
60 | 	body, err := getServiceGraphMetoroCall(ctx, request)
61 | 	if err != nil {
62 | 		return nil, fmt.Errorf("error getting service graph: %v", err)
63 | 	}
64 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
65 | }
66 | 
67 | func getServiceGraphMetoroCall(ctx context.Context, request GetServiceGraphRequest) ([]byte, error) {
68 | 	requestBody, err := json.Marshal(request)
69 | 	if err != nil {
70 | 		return nil, fmt.Errorf("error marshaling service graph request: %v", err)
71 | 	}
72 | 	return utils.MakeMetoroAPIRequest("POST", "serviceGraph", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
73 | }
74 | 
```

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

```go
  1 | /*
  2 | Metoro Alerts API
  3 | 
  4 | API for managing alerts in the Metoro observability platform.
  5 | 
  6 | API version: 1.0.0
  7 | */
  8 | 
  9 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
 10 | 
 11 | package model
 12 | 
 13 | import (
 14 | 	"encoding/json"
 15 | )
 16 | 
 17 | // checks if the TimeseriesSpecifierMetric type satisfies the MappedNullable interface at compile time
 18 | var _ MappedNullable = &TimeseriesSpecifierMetric{}
 19 | 
 20 | // TimeseriesSpecifierMetric Configuration for a metric timeseries
 21 | type TimeseriesSpecifierMetric struct {
 22 | 	// Name of the metric
 23 | 	MetricName *string `json:"metricName,omitempty"`
 24 | }
 25 | 
 26 | // NewTimeseriesSpecifierMetric instantiates a new TimeseriesSpecifierMetric object
 27 | // This constructor will assign default values to properties that have it defined,
 28 | // and makes sure properties required by API are set, but the set of arguments
 29 | // will change when the set of required properties is changed
 30 | func NewTimeseriesSpecifierMetric() *TimeseriesSpecifierMetric {
 31 | 	this := TimeseriesSpecifierMetric{}
 32 | 	return &this
 33 | }
 34 | 
 35 | // NewTimeseriesSpecifierMetricWithDefaults instantiates a new TimeseriesSpecifierMetric object
 36 | // This constructor will only assign default values to properties that have it defined,
 37 | // but it doesn't guarantee that properties required by API are set
 38 | func NewTimeseriesSpecifierMetricWithDefaults() *TimeseriesSpecifierMetric {
 39 | 	this := TimeseriesSpecifierMetric{}
 40 | 	return &this
 41 | }
 42 | 
 43 | // GetMetricName returns the MetricName field value if set, zero value otherwise.
 44 | func (o *TimeseriesSpecifierMetric) GetMetricName() string {
 45 | 	if o == nil || IsNil(o.MetricName) {
 46 | 		var ret string
 47 | 		return ret
 48 | 	}
 49 | 	return *o.MetricName
 50 | }
 51 | 
 52 | // GetMetricNameOk returns a tuple with the MetricName field value if set, nil otherwise
 53 | // and a boolean to check if the value has been set.
 54 | func (o *TimeseriesSpecifierMetric) GetMetricNameOk() (*string, bool) {
 55 | 	if o == nil || IsNil(o.MetricName) {
 56 | 		return nil, false
 57 | 	}
 58 | 	return o.MetricName, true
 59 | }
 60 | 
 61 | // HasMetricName returns a boolean if a field has been set.
 62 | func (o *TimeseriesSpecifierMetric) HasMetricName() bool {
 63 | 	if o != nil && !IsNil(o.MetricName) {
 64 | 		return true
 65 | 	}
 66 | 
 67 | 	return false
 68 | }
 69 | 
 70 | // SetMetricName gets a reference to the given string and assigns it to the MetricName field.
 71 | func (o *TimeseriesSpecifierMetric) SetMetricName(v string) {
 72 | 	o.MetricName = &v
 73 | }
 74 | 
 75 | func (o TimeseriesSpecifierMetric) MarshalJSON() ([]byte, error) {
 76 | 	toSerialize,err := o.ToMap()
 77 | 	if err != nil {
 78 | 		return []byte{}, err
 79 | 	}
 80 | 	return json.Marshal(toSerialize)
 81 | }
 82 | 
 83 | func (o TimeseriesSpecifierMetric) ToMap() (map[string]interface{}, error) {
 84 | 	toSerialize := map[string]interface{}{}
 85 | 	if !IsNil(o.MetricName) {
 86 | 		toSerialize["metricName"] = o.MetricName
 87 | 	}
 88 | 	return toSerialize, nil
 89 | }
 90 | 
 91 | type NullableTimeseriesSpecifierMetric struct {
 92 | 	value *TimeseriesSpecifierMetric
 93 | 	isSet bool
 94 | }
 95 | 
 96 | func (v NullableTimeseriesSpecifierMetric) Get() *TimeseriesSpecifierMetric {
 97 | 	return v.value
 98 | }
 99 | 
100 | func (v *NullableTimeseriesSpecifierMetric) Set(val *TimeseriesSpecifierMetric) {
101 | 	v.value = val
102 | 	v.isSet = true
103 | }
104 | 
105 | func (v NullableTimeseriesSpecifierMetric) IsSet() bool {
106 | 	return v.isSet
107 | }
108 | 
109 | func (v *NullableTimeseriesSpecifierMetric) Unset() {
110 | 	v.value = nil
111 | 	v.isSet = false
112 | }
113 | 
114 | func NewNullableTimeseriesSpecifierMetric(val *TimeseriesSpecifierMetric) *NullableTimeseriesSpecifierMetric {
115 | 	return &NullableTimeseriesSpecifierMetric{value: val, isSet: true}
116 | }
117 | 
118 | func (v NullableTimeseriesSpecifierMetric) MarshalJSON() ([]byte, error) {
119 | 	return json.Marshal(v.value)
120 | }
121 | 
122 | func (v *NullableTimeseriesSpecifierMetric) UnmarshalJSON(src []byte) error {
123 | 	v.isSet = true
124 | 	return json.Unmarshal(src, &v.value)
125 | }
126 | 
127 | 
128 | 
```

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

```go
  1 | package utils
  2 | 
  3 | import (
  4 | 	"fmt"
  5 | 	"os"
  6 | 	"strings"
  7 | 	"time"
  8 | )
  9 | 
 10 | // TimeWindow represents supported time window units
 11 | type TimeWindow string
 12 | 
 13 | const (
 14 | 	Minutes TimeWindow = "Minutes"
 15 | 	Hours   TimeWindow = "Hours"
 16 | 	Days    TimeWindow = "Days"
 17 | )
 18 | 
 19 | // TimeRangeType indicates whether the time range is relative or absolute
 20 | type TimeRangeType string
 21 | 
 22 | const (
 23 | 	RelativeTimeRange TimeRangeType = "relative"
 24 | 	AbsoluteTimeRange TimeRangeType = "absolute"
 25 | )
 26 | 
 27 | // TimeConfig holds the configuration for time range calculation
 28 | type TimeConfig struct {
 29 | 	// Type of time range (relative or absolute)
 30 | 	Type TimeRangeType `json:"type" jsonschema:"required,enum=relative,enum=absolute,description=Type of time range. Must be either 'relative' or 'absolute'"`
 31 | 
 32 | 	// Fields for relative time range
 33 | 	TimePeriod *int        `json:"time_period,omitempty" jsonschema:"description=For relative time range: the number of time units to look back"`
 34 | 	TimeWindow *TimeWindow `json:"time_window,omitempty" jsonschema:"description=For relative time range: the unit of time (Minutes, Hours, Days)"`
 35 | 
 36 | 	// Fields for absolute time range
 37 | 	StartTime *string `json:"start_time,omitempty" jsonschema:"description=For absolute time range: start time in RFC3339 format (e.g., '2024-12-12T14:27:22Z')"`
 38 | 	EndTime   *string `json:"end_time,omitempty" jsonschema:"description=For absolute time range: end time in RFC3339 format (e.g., '2024-12-12T14:27:22Z')"`
 39 | }
 40 | 
 41 | // CalculateTimeRange returns start and end timestamps based on the time configuration
 42 | func CalculateTimeRange(config TimeConfig) (startTime, endTime int64, err error) {
 43 | 	now := time.Now()
 44 | 	thirtyDaysAgo := now.Add(-30 * 24 * time.Hour)
 45 | 
 46 | 	switch config.Type {
 47 | 	case RelativeTimeRange:
 48 | 		if config.TimePeriod == nil || config.TimeWindow == nil {
 49 | 			return 0, 0, fmt.Errorf("time_period and time_window are required for relative time range")
 50 | 		}
 51 | 
 52 | 		var duration time.Duration
 53 | 		window := strings.ToLower(string(*config.TimeWindow))
 54 | 
 55 | 		switch window {
 56 | 		case "minutes", "minute", "min", "mins":
 57 | 			duration = time.Duration(*config.TimePeriod) * time.Minute
 58 | 		case "hours", "hour", "hr", "hrs":
 59 | 			duration = time.Duration(*config.TimePeriod) * time.Hour
 60 | 		case "days", "day":
 61 | 			duration = time.Duration(*config.TimePeriod) * 24 * time.Hour
 62 | 		default:
 63 | 			return 0, 0, fmt.Errorf("invalid time window: %s", *config.TimeWindow)
 64 | 		}
 65 | 
 66 | 		startTimeObj := now.Add(-duration)
 67 | 
 68 | 		// Check if the start time is more than 30 days ago in Prod.
 69 | 		if os.Getenv("IS_PROD") == "true" && startTimeObj.Before(thirtyDaysAgo) {
 70 | 			return 0, 0, fmt.Errorf("time range cannot exceed 30 days ago, please adjust the time_period or time_window")
 71 | 		}
 72 | 
 73 | 		return startTimeObj.Unix(), now.Unix(), nil
 74 | 
 75 | 	case AbsoluteTimeRange:
 76 | 		if config.StartTime == nil || config.EndTime == nil {
 77 | 			return 0, 0, fmt.Errorf("start_time and end_time are required for absolute time range")
 78 | 		}
 79 | 
 80 | 		startTimeObj, err := time.Parse(time.RFC3339, *config.StartTime)
 81 | 		if err != nil {
 82 | 			return 0, 0, fmt.Errorf("invalid start_time format: %v", err)
 83 | 		}
 84 | 
 85 | 		endTimeObj, err := time.Parse(time.RFC3339, *config.EndTime)
 86 | 		if err != nil {
 87 | 			return 0, 0, fmt.Errorf("invalid end_time format: %v", err)
 88 | 		}
 89 | 
 90 | 		if endTimeObj.Before(startTimeObj) {
 91 | 			return 0, 0, fmt.Errorf("end_time cannot be before start_time")
 92 | 		}
 93 | 
 94 | 		// Check if the start time is more than 30 days ago
 95 | 		if os.Getenv("IS_PROD") == "true" && startTimeObj.Before(thirtyDaysAgo) {
 96 | 			return 0, 0, fmt.Errorf("time range cannot exceed 30 days")
 97 | 		}
 98 | 
 99 | 		return startTimeObj.Unix(), endTimeObj.Unix(), nil
100 | 
101 | 	default:
102 | 		return 0, 0, fmt.Errorf("invalid time range type: %s", config.Type)
103 | 	}
104 | }
105 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetLogAttributeValuesHandlerArgs struct {
15 | 	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"`
16 | 	Attribute      string              `json:"attribute" jsonschema:"required,description=The attribute key to get the possible values for"`
17 | 	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]}"`
18 | 	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]}"`
19 | 	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']"`
20 | 	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']"`
21 | 	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"`
22 | }
23 | 
24 | func GetLogAttributeValuesForIndividualAttributeHandler(ctx context.Context, arguments GetLogAttributeValuesHandlerArgs) (*mcpgolang.ToolResponse, error) {
25 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
26 | 	if err != nil {
27 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
28 | 	}
29 | 	request := model.GetSingleLogSummaryRequest{
30 | 		LogSummaryRequest: model.LogSummaryRequest{
31 | 			StartTime:      startTime,
32 | 			EndTime:        endTime,
33 | 			Filters:        arguments.Filters,
34 | 			ExcludeFilters: arguments.ExcludeFilters,
35 | 			Regexes:        arguments.Regexes,
36 | 			ExcludeRegexes: arguments.ExcludeRegexes,
37 | 			Environments:   arguments.Environments,
38 | 		},
39 | 		Attribute: arguments.Attribute,
40 | 	}
41 | 
42 | 	jsonBody, err := json.Marshal(request)
43 | 	if err != nil {
44 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
45 | 	}
46 | 
47 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "logsSummaryIndividualAttribute", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
48 | 	if err != nil {
49 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
50 | 	}
51 | 
52 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
53 | }
54 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetTraceAttributeValuesHandlerArgs struct {
15 | 	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"`
16 | 	Attribute      string              `json:"attribute" jsonschema:"required,description=The name of the attribute key to get the possible values for"`
17 | 	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]}"`
18 | 	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]}"`
19 | 	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']"`
20 | 	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']"`
21 | 	Environments   []string            `json:"environments" jsonschema:"description=The environments to get traces from. If empty traces from all environments will be returned"`
22 | }
23 | 
24 | func GetTraceAttributeValuesForIndividualAttributeHandler(ctx context.Context, arguments GetTraceAttributeValuesHandlerArgs) (*mcpgolang.ToolResponse, error) {
25 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
26 | 	if err != nil {
27 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
28 | 	}
29 | 	request := model.GetSingleTraceSummaryRequest{
30 | 		TracesSummaryRequest: model.TracesSummaryRequest{
31 | 			StartTime:      startTime,
32 | 			EndTime:        endTime,
33 | 			Filters:        arguments.Filters,
34 | 			ExcludeFilters: arguments.ExcludeFilters,
35 | 			Regexes:        arguments.Regexes,
36 | 			ExcludeRegexes: arguments.ExcludeRegexes,
37 | 			Environments:   arguments.Environments,
38 | 		},
39 | 		Attribute: arguments.Attribute,
40 | 	}
41 | 
42 | 	jsonBody, err := json.Marshal(request)
43 | 	if err != nil {
44 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
45 | 	}
46 | 
47 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "tracesSummaryIndividualAttribute", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
48 | 	if err != nil {
49 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
50 | 	}
51 | 
52 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
53 | }
54 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 	"time"
 9 | 
10 | 	mcpgolang "github.com/metoro-io/mcp-golang"
11 | 	"github.com/metoro-io/metoro-mcp-server/model"
12 | 	"github.com/metoro-io/metoro-mcp-server/utils"
13 | )
14 | 
15 | type UpdateInvestigationHandlerArgs struct {
16 | 	InvestigationUUID  string           `json:"investigationUuid" jsonschema:"required,description=UUID of the investigation to update"`
17 | 	Title              string           `json:"title" jsonschema:"required,description=Title of the investigation"`
18 | 	Summary            string           `json:"summary" jsonschema:"description=Summary of the investigation - should be at most 3 sentences"`
19 | 	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."`
20 | 	ServiceName        *string          `json:"serviceName,omitempty" jsonschema:"description=Optional root cause service name to associate with this investigation."`
21 | 	Markdown           string           `json:"markdown" jsonschema:"required,description=Markdown content of the investigation"`
22 | 	InProgress         *bool            `json:"inProgress" jsonschema:"description=Whether the investigation is in progress or not. Defaults to false"`
23 | 	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"`
24 | 	ChatHistoryUUID    *string          `json:"chatHistoryUuid,omitempty" jsonschema:"description=Optional chat history UUID to associate with this investigation"`
25 | 	IssueUUID          *string          `json:"issueUuid,omitempty" jsonschema:"description=Optional related AI issue UUID for this investigation"`
26 | }
27 | 
28 | func UpdateInvestigationHandler(ctx context.Context, arguments UpdateInvestigationHandlerArgs) (*mcpgolang.ToolResponse, error) {
29 | 	// Create the request body
30 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
31 | 	if err != nil {
32 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
33 | 	}
34 | 
35 | 	falsePtr := false
36 | 	reviewRequiredPtr := "ReviewRequired"
37 | 	start := time.Unix(startTime, 0)
38 | 	end := time.Unix(endTime, 0)
39 | 
40 | 	tags := make(map[string]string)
41 | 	if arguments.ServiceName != nil {
42 | 		tags["service"] = *arguments.ServiceName
43 | 	}
44 | 
45 | 	title := arguments.Title
46 | 	summary := arguments.Summary
47 | 	markdown := arguments.Markdown
48 | 	tagsPtr := tags
49 | 
50 | 	request := model.UpdateInvestigationRequest{
51 | 		Title:                &title,
52 | 		Summary:              &summary,
53 | 		Markdown:             &markdown,
54 | 		Tags:                 &tagsPtr,
55 | 		IssueStartTime:       &start,
56 | 		IssueEndTime:         &end,
57 | 		ChatHistoryUUID:      arguments.ChatHistoryUUID,
58 | 		IsVisible:            &falsePtr,
59 | 		InProgress:           arguments.InProgress,
60 | 		MetoroApprovalStatus: &reviewRequiredPtr,
61 | 		IssueUUID:            arguments.IssueUUID,
62 | 		RecommendedActions:   arguments.RecommendedActions,
63 | 	}
64 | 
65 | 	requestBody, err := json.Marshal(request)
66 | 	if err != nil {
67 | 		return nil, fmt.Errorf("failed to marshal request: %w", err)
68 | 	}
69 | 
70 | 	// Make the API request - using PUT method for update
71 | 	endpoint := fmt.Sprintf("investigation?uuid=%s", arguments.InvestigationUUID)
72 | 	responseBody, err := utils.MakeMetoroAPIRequest("PUT", endpoint, bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
73 | 	if err != nil {
74 | 		return nil, fmt.Errorf("failed to update investigation: %w", err)
75 | 	}
76 | 
77 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
78 | }
79 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 	"time"
 9 | 
10 | 	mcpgolang "github.com/metoro-io/mcp-golang"
11 | 	"github.com/metoro-io/metoro-mcp-server/model"
12 | 	"github.com/metoro-io/metoro-mcp-server/utils"
13 | )
14 | 
15 | type CreateInvestigationHandlerArgs struct {
16 | 	Title              string           `json:"title" jsonschema:"required,description=Title of the investigation"`
17 | 	Summary            string           `json:"summary" jsonschema:"description=Summary of the investigation - should be at most 3 sentences"`
18 | 	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."`
19 | 	ServiceName        *string          `json:"serviceName,omitempty" jsonschema:"description=Optional root cause service name to associate with this investigation."`
20 | 	Markdown           string           `json:"markdown" jsonschema:"required,description=Markdown content of the investigation"`
21 | 	InProgress         *bool            `json:"inProgress" jsonschema:"description=Whether the investigation is in progress or not. Defaults to false"`
22 | 	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"`
23 | 	ChatHistoryUUID    *string          `json:"chatHistoryUuid,omitempty" jsonschema:"description=Optional chat history UUID to associate with this investigation"`
24 | 	IssueUUID          *string          `json:"issueUuid,omitempty" jsonschema:"description=Optional related AI issue UUID for this investigation"`
25 | 	AlertFireUUID      *string          `json:"alertFireUuid,omitempty" jsonschema:"description=Optional alert fire UUID to associate with this investigation"`
26 | 	AlertUUID          *string          `json:"alertUuid,omitempty" jsonschema:"description=Optional alert UUID to associate with this investigation"`
27 | }
28 | 
29 | func CreateInvestigationHandler(ctx context.Context, arguments CreateInvestigationHandlerArgs) (*mcpgolang.ToolResponse, error) {
30 | 	// Create the request body
31 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
32 | 	if err != nil {
33 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
34 | 	}
35 | 
36 | 	falsePtr := false
37 | 	reviewRequiredPtr := "ReviewRequired"
38 | 	start := time.Unix(startTime, 0)
39 | 	end := time.Unix(endTime, 0)
40 | 	tags := make(map[string]string)
41 | 	if arguments.ServiceName != nil {
42 | 		tags["service"] = *arguments.ServiceName
43 | 	}
44 | 	request := model.CreateInvestigationRequest{
45 | 		Title:                arguments.Title,
46 | 		Summary:              arguments.Summary,
47 | 		RecommendedActions:   arguments.RecommendedActions,
48 | 		Markdown:             arguments.Markdown,
49 | 		Tags:                 tags,
50 | 		IssueStartTime:       &start,
51 | 		IssueEndTime:         &end,
52 | 		ChatHistoryUUID:      arguments.ChatHistoryUUID,
53 | 		IsVisible:            &falsePtr,
54 | 		InProgress:           arguments.InProgress,
55 | 		MetoroApprovalStatus: &reviewRequiredPtr,
56 | 		IssueUUID:            arguments.IssueUUID,
57 | 		AlertFireUUID:        arguments.AlertFireUUID,
58 | 		AlertUUID:            arguments.AlertUUID,
59 | 	}
60 | 
61 | 	requestBody, err := json.Marshal(request)
62 | 	if err != nil {
63 | 		return nil, fmt.Errorf("failed to marshal request: %w", err)
64 | 	}
65 | 
66 | 	// Make the API request
67 | 	responseBody, err := utils.MakeMetoroAPIRequest("POST", "investigation", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
68 | 	if err != nil {
69 | 		return nil, fmt.Errorf("failed to create investigation: %w", err)
70 | 	}
71 | 
72 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(responseBody))), nil
73 | }
74 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetK8sEventAttributeValueHandlerArgs struct {
15 | 	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"`
16 | 	Attribute      string              `json:"attribute" jsonschema:"required,description=The attribute key to get the possible values for"`
17 | 	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]}"`
18 | 	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]}"`
19 | 	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']"`
20 | 	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']"`
21 | 	Environments   []string            `json:"environments" jsonschema:"description=The environments to get events from. If empty events from all environments will be returned"`
22 | 	Ascending      bool                `json:"ascending" jsonschema:"description=If true events will be returned in ascending order otherwise in descending order"`
23 | }
24 | 
25 | func GetK8sEventAttributeValuesForIndividualAttributeHandler(ctx context.Context, arguments GetK8sEventAttributeValueHandlerArgs) (*mcpgolang.ToolResponse, error) {
26 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
27 | 	if err != nil {
28 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
29 | 	}
30 | 	request := model.GetSingleK8sEventSummaryRequest{
31 | 		GetK8sEventsRequest: model.GetK8sEventsRequest{
32 | 			StartTime:      startTime,
33 | 			EndTime:        endTime,
34 | 			Filters:        arguments.Filters,
35 | 			ExcludeFilters: arguments.ExcludeFilters,
36 | 			Regexes:        arguments.Regexes,
37 | 			ExcludeRegexes: arguments.ExcludeRegexes,
38 | 			Environments:   arguments.Environments,
39 | 			Ascending:      arguments.Ascending,
40 | 		},
41 | 		Attribute: arguments.Attribute,
42 | 	}
43 | 
44 | 	jsonBody, err := json.Marshal(request)
45 | 	if err != nil {
46 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
47 | 	}
48 | 
49 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "k8s/events/summaryIndividualAttribute", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
50 | 	if err != nil {
51 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
52 | 	}
53 | 
54 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
55 | }
56 | 
```

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

```go
  1 | package tools
  2 | 
  3 | import (
  4 | 	"bytes"
  5 | 	"context"
  6 | 	"encoding/json"
  7 | 	"fmt"
  8 | 	mcpgolang "github.com/metoro-io/mcp-golang"
  9 | 	"github.com/metoro-io/metoro-mcp-server/model"
 10 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 11 | )
 12 | 
 13 | type GetAttributeValuesHandlerArgs struct {
 14 | 	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'"`
 15 | 	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"`
 16 | 	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"`
 17 | 	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."`
 18 | 	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]}"`
 19 | }
 20 | 
 21 | func GetAttributeValuesHandler(ctx context.Context, arguments GetAttributeValuesHandlerArgs) (*mcpgolang.ToolResponse, error) {
 22 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
 23 | 	if err != nil {
 24 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
 25 | 	}
 26 | 
 27 | 	request := model.GetAttributeValuesRequest{
 28 | 		Type:      arguments.Type,
 29 | 		Attribute: arguments.Attribute,
 30 | 	}
 31 | 
 32 | 	switch arguments.Type {
 33 | 	case model.Logs:
 34 | 		err = CheckAttributes(ctx, arguments.Type, arguments.Filters, map[string][]string{}, []string{}, nil)
 35 | 		if err != nil {
 36 | 			return nil, err
 37 | 		}
 38 | 		modelRequest := model.LogSummaryRequest{
 39 | 			StartTime: startTime,
 40 | 			EndTime:   endTime,
 41 | 			Filters:   arguments.Filters,
 42 | 		}
 43 | 		request.Logs = &modelRequest
 44 | 		break
 45 | 	case model.Trace:
 46 | 		err = CheckAttributes(ctx, arguments.Type, arguments.Filters, map[string][]string{}, []string{}, nil)
 47 | 		if err != nil {
 48 | 			return nil, err
 49 | 		}
 50 | 		modelRequest := model.TracesSummaryRequest{
 51 | 			StartTime: startTime,
 52 | 			EndTime:   endTime,
 53 | 			Filters:   arguments.Filters,
 54 | 		}
 55 | 		request.Trace = &modelRequest
 56 | 		break
 57 | 	case model.Metric:
 58 | 		err = CheckAttributes(ctx, arguments.Type, arguments.Filters, map[string][]string{}, []string{}, &model.GetMetricAttributesRequest{
 59 | 			StartTime:  startTime,
 60 | 			EndTime:    endTime,
 61 | 			MetricName: arguments.MetricName,
 62 | 		})
 63 | 		if err != nil {
 64 | 			return nil, err
 65 | 		}
 66 | 		modelRequest := model.GetMetricAttributesRequest{
 67 | 			StartTime:    startTime,
 68 | 			EndTime:      endTime,
 69 | 			MetricName:   arguments.MetricName,
 70 | 			Environments: arguments.Filters["environment"],
 71 | 		}
 72 | 		request.Metric = &modelRequest
 73 | 		break
 74 | 	//case model.KubernetesResource:
 75 | 	//
 76 | 	//	modelRequest := model.GetKubernetesResourceRequest{
 77 | 	//		StartTime:      startTime,
 78 | 	//		EndTime:        endTime,
 79 | 	//		Filters:        arguments.Filters,
 80 | 	//		ExcludeFilters: arguments.Filters,
 81 | 	//	}
 82 | 	//	request.Kubernetes = &modelRequest
 83 | 	//	break
 84 | 	default:
 85 | 		return nil, fmt.Errorf("invalid type: %v", arguments.Type)
 86 | 	}
 87 | 	jsonBody, err := json.Marshal(request)
 88 | 	if err != nil {
 89 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
 90 | 	}
 91 | 
 92 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "metrics/attribute/values", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
 93 | 
 94 | 	if err != nil {
 95 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
 96 | 	}
 97 | 
 98 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(resp)))), nil
 99 | }
100 | 
```

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

```go
  1 | /*
  2 | Metoro Alerts API
  3 | 
  4 | API for managing alerts in the Metoro observability platform.
  5 | 
  6 | API version: 1.0.0
  7 | */
  8 | 
  9 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
 10 | 
 11 | package model
 12 | 
 13 | import (
 14 | 	"encoding/json"
 15 | )
 16 | 
 17 | // checks if the TimeseriesSpecifierKubernetesResource type satisfies the MappedNullable interface at compile time
 18 | var _ MappedNullable = &TimeseriesSpecifierKubernetesResource{}
 19 | 
 20 | // TimeseriesSpecifierKubernetesResource Configuration for a Kubernetes resource timeseries
 21 | type TimeseriesSpecifierKubernetesResource struct {
 22 | 	// JSONPath expression to extract data from Kubernetes resources
 23 | 	JsonPath *string `json:"jsonPath,omitempty"`
 24 | }
 25 | 
 26 | // NewTimeseriesSpecifierKubernetesResource instantiates a new TimeseriesSpecifierKubernetesResource object
 27 | // This constructor will assign default values to properties that have it defined,
 28 | // and makes sure properties required by API are set, but the set of arguments
 29 | // will change when the set of required properties is changed
 30 | func NewTimeseriesSpecifierKubernetesResource() *TimeseriesSpecifierKubernetesResource {
 31 | 	this := TimeseriesSpecifierKubernetesResource{}
 32 | 	return &this
 33 | }
 34 | 
 35 | // NewTimeseriesSpecifierKubernetesResourceWithDefaults instantiates a new TimeseriesSpecifierKubernetesResource object
 36 | // This constructor will only assign default values to properties that have it defined,
 37 | // but it doesn't guarantee that properties required by API are set
 38 | func NewTimeseriesSpecifierKubernetesResourceWithDefaults() *TimeseriesSpecifierKubernetesResource {
 39 | 	this := TimeseriesSpecifierKubernetesResource{}
 40 | 	return &this
 41 | }
 42 | 
 43 | // GetJsonPath returns the JsonPath field value if set, zero value otherwise.
 44 | func (o *TimeseriesSpecifierKubernetesResource) GetJsonPath() string {
 45 | 	if o == nil || IsNil(o.JsonPath) {
 46 | 		var ret string
 47 | 		return ret
 48 | 	}
 49 | 	return *o.JsonPath
 50 | }
 51 | 
 52 | // GetJsonPathOk returns a tuple with the JsonPath field value if set, nil otherwise
 53 | // and a boolean to check if the value has been set.
 54 | func (o *TimeseriesSpecifierKubernetesResource) GetJsonPathOk() (*string, bool) {
 55 | 	if o == nil || IsNil(o.JsonPath) {
 56 | 		return nil, false
 57 | 	}
 58 | 	return o.JsonPath, true
 59 | }
 60 | 
 61 | // HasJsonPath returns a boolean if a field has been set.
 62 | func (o *TimeseriesSpecifierKubernetesResource) HasJsonPath() bool {
 63 | 	if o != nil && !IsNil(o.JsonPath) {
 64 | 		return true
 65 | 	}
 66 | 
 67 | 	return false
 68 | }
 69 | 
 70 | // SetJsonPath gets a reference to the given string and assigns it to the JsonPath field.
 71 | func (o *TimeseriesSpecifierKubernetesResource) SetJsonPath(v string) {
 72 | 	o.JsonPath = &v
 73 | }
 74 | 
 75 | func (o TimeseriesSpecifierKubernetesResource) MarshalJSON() ([]byte, error) {
 76 | 	toSerialize,err := o.ToMap()
 77 | 	if err != nil {
 78 | 		return []byte{}, err
 79 | 	}
 80 | 	return json.Marshal(toSerialize)
 81 | }
 82 | 
 83 | func (o TimeseriesSpecifierKubernetesResource) ToMap() (map[string]interface{}, error) {
 84 | 	toSerialize := map[string]interface{}{}
 85 | 	if !IsNil(o.JsonPath) {
 86 | 		toSerialize["jsonPath"] = o.JsonPath
 87 | 	}
 88 | 	return toSerialize, nil
 89 | }
 90 | 
 91 | type NullableTimeseriesSpecifierKubernetesResource struct {
 92 | 	value *TimeseriesSpecifierKubernetesResource
 93 | 	isSet bool
 94 | }
 95 | 
 96 | func (v NullableTimeseriesSpecifierKubernetesResource) Get() *TimeseriesSpecifierKubernetesResource {
 97 | 	return v.value
 98 | }
 99 | 
100 | func (v *NullableTimeseriesSpecifierKubernetesResource) Set(val *TimeseriesSpecifierKubernetesResource) {
101 | 	v.value = val
102 | 	v.isSet = true
103 | }
104 | 
105 | func (v NullableTimeseriesSpecifierKubernetesResource) IsSet() bool {
106 | 	return v.isSet
107 | }
108 | 
109 | func (v *NullableTimeseriesSpecifierKubernetesResource) Unset() {
110 | 	v.value = nil
111 | 	v.isSet = false
112 | }
113 | 
114 | func NewNullableTimeseriesSpecifierKubernetesResource(val *TimeseriesSpecifierKubernetesResource) *NullableTimeseriesSpecifierKubernetesResource {
115 | 	return &NullableTimeseriesSpecifierKubernetesResource{value: val, isSet: true}
116 | }
117 | 
118 | func (v NullableTimeseriesSpecifierKubernetesResource) MarshalJSON() ([]byte, error) {
119 | 	return json.Marshal(v.value)
120 | }
121 | 
122 | func (v *NullableTimeseriesSpecifierKubernetesResource) UnmarshalJSON(src []byte) error {
123 | 	v.isSet = true
124 | 	return json.Unmarshal(src, &v.value)
125 | }
126 | 
127 | 
128 | 
```

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

```go
  1 | /*
  2 | Metoro Alerts API
  3 | 
  4 | API for managing alerts in the Metoro observability platform.
  5 | 
  6 | API version: 1.0.0
  7 | */
  8 | 
  9 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
 10 | 
 11 | package model
 12 | 
 13 | import (
 14 | 	"encoding/json"
 15 | 	"fmt"
 16 | 	"gopkg.in/validator.v2"
 17 | )
 18 | 
 19 | // TimeseriesConfigExpression - Expression defining the metrics to monitor
 20 | type TimeseriesConfigExpression struct {
 21 | 	ExpressionConfig *ExpressionConfig
 22 | 	String *string
 23 | }
 24 | 
 25 | // ExpressionConfigAsTimeseriesConfigExpression is a convenience function that returns ExpressionConfig wrapped in TimeseriesConfigExpression
 26 | func ExpressionConfigAsTimeseriesConfigExpression(v *ExpressionConfig) TimeseriesConfigExpression {
 27 | 	return TimeseriesConfigExpression{
 28 | 		ExpressionConfig: v,
 29 | 	}
 30 | }
 31 | 
 32 | // stringAsTimeseriesConfigExpression is a convenience function that returns string wrapped in TimeseriesConfigExpression
 33 | func StringAsTimeseriesConfigExpression(v *string) TimeseriesConfigExpression {
 34 | 	return TimeseriesConfigExpression{
 35 | 		String: v,
 36 | 	}
 37 | }
 38 | 
 39 | 
 40 | // Unmarshal JSON data into one of the pointers in the struct
 41 | func (dst *TimeseriesConfigExpression) UnmarshalJSON(data []byte) error {
 42 | 	var err error
 43 | 	match := 0
 44 | 	// try to unmarshal data into ExpressionConfig
 45 | 	err = newStrictDecoder(data).Decode(&dst.ExpressionConfig)
 46 | 	if err == nil {
 47 | 		jsonExpressionConfig, _ := json.Marshal(dst.ExpressionConfig)
 48 | 		if string(jsonExpressionConfig) == "{}" { // empty struct
 49 | 			dst.ExpressionConfig = nil
 50 | 		} else {
 51 | 			if err = validator.Validate(dst.ExpressionConfig); err != nil {
 52 | 				dst.ExpressionConfig = nil
 53 | 			} else {
 54 | 				match++
 55 | 			}
 56 | 		}
 57 | 	} else {
 58 | 		dst.ExpressionConfig = nil
 59 | 	}
 60 | 
 61 | 	// try to unmarshal data into String
 62 | 	err = newStrictDecoder(data).Decode(&dst.String)
 63 | 	if err == nil {
 64 | 		jsonString, _ := json.Marshal(dst.String)
 65 | 		if string(jsonString) == "{}" { // empty struct
 66 | 			dst.String = nil
 67 | 		} else {
 68 | 			if err = validator.Validate(dst.String); err != nil {
 69 | 				dst.String = nil
 70 | 			} else {
 71 | 				match++
 72 | 			}
 73 | 		}
 74 | 	} else {
 75 | 		dst.String = nil
 76 | 	}
 77 | 
 78 | 	if match > 1 { // more than 1 match
 79 | 		// reset to nil
 80 | 		dst.ExpressionConfig = nil
 81 | 		dst.String = nil
 82 | 
 83 | 		return fmt.Errorf("data matches more than one schema in oneOf(TimeseriesConfigExpression)")
 84 | 	} else if match == 1 {
 85 | 		return nil // exactly one match
 86 | 	} else { // no match
 87 | 		return fmt.Errorf("data failed to match schemas in oneOf(TimeseriesConfigExpression)")
 88 | 	}
 89 | }
 90 | 
 91 | // Marshal data from the first non-nil pointers in the struct to JSON
 92 | func (src TimeseriesConfigExpression) MarshalJSON() ([]byte, error) {
 93 | 	if src.ExpressionConfig != nil {
 94 | 		return json.Marshal(&src.ExpressionConfig)
 95 | 	}
 96 | 
 97 | 	if src.String != nil {
 98 | 		return json.Marshal(&src.String)
 99 | 	}
100 | 
101 | 	return nil, nil // no data in oneOf schemas
102 | }
103 | 
104 | // Get the actual instance
105 | func (obj *TimeseriesConfigExpression) GetActualInstance() (interface{}) {
106 | 	if obj == nil {
107 | 		return nil
108 | 	}
109 | 	if obj.ExpressionConfig != nil {
110 | 		return obj.ExpressionConfig
111 | 	}
112 | 
113 | 	if obj.String != nil {
114 | 		return obj.String
115 | 	}
116 | 
117 | 	// all schemas are nil
118 | 	return nil
119 | }
120 | 
121 | // Get the actual instance value
122 | func (obj TimeseriesConfigExpression) GetActualInstanceValue() (interface{}) {
123 | 	if obj.ExpressionConfig != nil {
124 | 		return *obj.ExpressionConfig
125 | 	}
126 | 
127 | 	if obj.String != nil {
128 | 		return *obj.String
129 | 	}
130 | 
131 | 	// all schemas are nil
132 | 	return nil
133 | }
134 | 
135 | type NullableTimeseriesConfigExpression struct {
136 | 	value *TimeseriesConfigExpression
137 | 	isSet bool
138 | }
139 | 
140 | func (v NullableTimeseriesConfigExpression) Get() *TimeseriesConfigExpression {
141 | 	return v.value
142 | }
143 | 
144 | func (v *NullableTimeseriesConfigExpression) Set(val *TimeseriesConfigExpression) {
145 | 	v.value = val
146 | 	v.isSet = true
147 | }
148 | 
149 | func (v NullableTimeseriesConfigExpression) IsSet() bool {
150 | 	return v.isSet
151 | }
152 | 
153 | func (v *NullableTimeseriesConfigExpression) Unset() {
154 | 	v.value = nil
155 | 	v.isSet = false
156 | }
157 | 
158 | func NewNullableTimeseriesConfigExpression(val *TimeseriesConfigExpression) *NullableTimeseriesConfigExpression {
159 | 	return &NullableTimeseriesConfigExpression{value: val, isSet: true}
160 | }
161 | 
162 | func (v NullableTimeseriesConfigExpression) MarshalJSON() ([]byte, error) {
163 | 	return json.Marshal(v.value)
164 | }
165 | 
166 | func (v *NullableTimeseriesConfigExpression) UnmarshalJSON(src []byte) error {
167 | 	v.isSet = true
168 | 	return json.Unmarshal(src, &v.value)
169 | }
170 | 
171 | 
172 | 
```

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

```go
  1 | /*
  2 | Metoro API
  3 | 
  4 | API for managing Metoro environments, alerts, and dashboards.
  5 | 
  6 | API version: 1.0.0
  7 | */
  8 | 
  9 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
 10 | 
 11 | package model
 12 | 
 13 | import (
 14 | 	"bytes"
 15 | 	"encoding/json"
 16 | 	"fmt"
 17 | )
 18 | 
 19 | // checks if the CreateUpdateAlertRequest type satisfies the MappedNullable interface at compile time
 20 | var _ MappedNullable = &CreateUpdateAlertRequest{}
 21 | 
 22 | // 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.
 23 | type CreateUpdateAlertRequest struct {
 24 | 	Alert Alert `json:"alert"`
 25 | }
 26 | 
 27 | type _CreateUpdateAlertRequest CreateUpdateAlertRequest
 28 | 
 29 | // NewCreateUpdateAlertRequest instantiates a new CreateUpdateAlertRequest object
 30 | // This constructor will assign default values to properties that have it defined,
 31 | // and makes sure properties required by API are set, but the set of arguments
 32 | // will change when the set of required properties is changed
 33 | func NewCreateUpdateAlertRequest(alert Alert) *CreateUpdateAlertRequest {
 34 | 	this := CreateUpdateAlertRequest{}
 35 | 	this.Alert = alert
 36 | 	return &this
 37 | }
 38 | 
 39 | // NewCreateUpdateAlertRequestWithDefaults instantiates a new CreateUpdateAlertRequest object
 40 | // This constructor will only assign default values to properties that have it defined,
 41 | // but it doesn't guarantee that properties required by API are set
 42 | func NewCreateUpdateAlertRequestWithDefaults() *CreateUpdateAlertRequest {
 43 | 	this := CreateUpdateAlertRequest{}
 44 | 	return &this
 45 | }
 46 | 
 47 | // GetAlert returns the Alert field value
 48 | func (o *CreateUpdateAlertRequest) GetAlert() Alert {
 49 | 	if o == nil {
 50 | 		var ret Alert
 51 | 		return ret
 52 | 	}
 53 | 
 54 | 	return o.Alert
 55 | }
 56 | 
 57 | // GetAlertOk returns a tuple with the Alert field value
 58 | // and a boolean to check if the value has been set.
 59 | func (o *CreateUpdateAlertRequest) GetAlertOk() (*Alert, bool) {
 60 | 	if o == nil {
 61 | 		return nil, false
 62 | 	}
 63 | 	return &o.Alert, true
 64 | }
 65 | 
 66 | // SetAlert sets field value
 67 | func (o *CreateUpdateAlertRequest) SetAlert(v Alert) {
 68 | 	o.Alert = v
 69 | }
 70 | 
 71 | func (o CreateUpdateAlertRequest) MarshalJSON() ([]byte, error) {
 72 | 	toSerialize, err := o.ToMap()
 73 | 	if err != nil {
 74 | 		return []byte{}, err
 75 | 	}
 76 | 	return json.Marshal(toSerialize)
 77 | }
 78 | 
 79 | func (o CreateUpdateAlertRequest) ToMap() (map[string]interface{}, error) {
 80 | 	toSerialize := map[string]interface{}{}
 81 | 	toSerialize["alert"] = o.Alert
 82 | 	return toSerialize, nil
 83 | }
 84 | 
 85 | func (o *CreateUpdateAlertRequest) UnmarshalJSON(data []byte) (err error) {
 86 | 	// This validates that all required properties are included in the JSON object
 87 | 	// by unmarshalling the object into a generic map with string keys and checking
 88 | 	// that every required field exists as a key in the generic map.
 89 | 	requiredProperties := []string{
 90 | 		"alert",
 91 | 	}
 92 | 
 93 | 	allProperties := make(map[string]interface{})
 94 | 
 95 | 	err = json.Unmarshal(data, &allProperties)
 96 | 
 97 | 	if err != nil {
 98 | 		return err
 99 | 	}
100 | 
101 | 	for _, requiredProperty := range requiredProperties {
102 | 		if _, exists := allProperties[requiredProperty]; !exists {
103 | 			return fmt.Errorf("no value given for required property %v", requiredProperty)
104 | 		}
105 | 	}
106 | 
107 | 	varCreateUpdateAlertRequest := _CreateUpdateAlertRequest{}
108 | 
109 | 	decoder := json.NewDecoder(bytes.NewReader(data))
110 | 	decoder.DisallowUnknownFields()
111 | 	err = decoder.Decode(&varCreateUpdateAlertRequest)
112 | 
113 | 	if err != nil {
114 | 		return err
115 | 	}
116 | 
117 | 	*o = CreateUpdateAlertRequest(varCreateUpdateAlertRequest)
118 | 
119 | 	return err
120 | }
121 | 
122 | type NullableCreateUpdateAlertRequest struct {
123 | 	value *CreateUpdateAlertRequest
124 | 	isSet bool
125 | }
126 | 
127 | func (v NullableCreateUpdateAlertRequest) Get() *CreateUpdateAlertRequest {
128 | 	return v.value
129 | }
130 | 
131 | func (v *NullableCreateUpdateAlertRequest) Set(val *CreateUpdateAlertRequest) {
132 | 	v.value = val
133 | 	v.isSet = true
134 | }
135 | 
136 | func (v NullableCreateUpdateAlertRequest) IsSet() bool {
137 | 	return v.isSet
138 | }
139 | 
140 | func (v *NullableCreateUpdateAlertRequest) Unset() {
141 | 	v.value = nil
142 | 	v.isSet = false
143 | }
144 | 
145 | func NewNullableCreateUpdateAlertRequest(val *CreateUpdateAlertRequest) *NullableCreateUpdateAlertRequest {
146 | 	return &NullableCreateUpdateAlertRequest{value: val, isSet: true}
147 | }
148 | 
149 | func (v NullableCreateUpdateAlertRequest) MarshalJSON() ([]byte, error) {
150 | 	return json.Marshal(v.value)
151 | }
152 | 
153 | func (v *NullableCreateUpdateAlertRequest) UnmarshalJSON(src []byte) error {
154 | 	v.isSet = true
155 | 	return json.Unmarshal(src, &v.value)
156 | }
157 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetTracesHandlerArgs struct {
15 | 	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."`
16 | 	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"`
17 | 	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"`
18 | }
19 | 
20 | func GetTracesHandler(ctx context.Context, arguments GetTracesHandlerArgs) (*mcpgolang.ToolResponse, error) {
21 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
22 | 	if err != nil {
23 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
24 | 	}
25 | 
26 | 	err = CheckAttributes(ctx, model.Trace, arguments.Filters, arguments.ExcludeFilters, []string{}, nil)
27 | 	if err != nil {
28 | 		return nil, err
29 | 	}
30 | 
31 | 	limit := 20
32 | 
33 | 	request := model.GetTracesRequest{
34 | 		StartTime:      startTime,
35 | 		EndTime:        endTime,
36 | 		Filters:        arguments.Filters,
37 | 		ExcludeFilters: arguments.ExcludeFilters,
38 | 		Limit:          &limit,
39 | 	}
40 | 
41 | 	body, err := getTracesMetoroCall(ctx, request)
42 | 	if err != nil {
43 | 		return nil, fmt.Errorf("error getting traces: %v", err)
44 | 	}
45 | 
46 | 	// Add human readable duration to the response
47 | 	bodyWithDuration, err := addHumanReadableDuration(body)
48 | 	if err != nil {
49 | 		return nil, fmt.Errorf("error adding human readable duration: %v", err)
50 | 	}
51 | 
52 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(bodyWithDuration)))), nil
53 | }
54 | 
55 | func getTracesMetoroCall(ctx context.Context, request model.GetTracesRequest) ([]byte, error) {
56 | 	requestBody, err := json.Marshal(request)
57 | 	if err != nil {
58 | 		return nil, fmt.Errorf("error marshaling traces request: %v", err)
59 | 	}
60 | 	return utils.MakeMetoroAPIRequest("POST", "traces", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
61 | }
62 | 
63 | func addHumanReadableDuration(response []byte) ([]byte, error) {
64 | 	var tracesResponse model.GetTracesResponse
65 | 	err := json.Unmarshal(response, &tracesResponse)
66 | 	if err != nil {
67 | 		return nil, fmt.Errorf("error unmarshaling get traces response: %v", err)
68 | 	}
69 | 
70 | 	// Add human readable duration to each trace
71 | 	for i := range tracesResponse.Traces {
72 | 		trace := &tracesResponse.Traces[i]
73 | 		durationNs := trace.Duration
74 | 
75 | 		// Convert duration to human readable format
76 | 		var humanReadable string
77 | 		switch {
78 | 		case durationNs < 1000: // Less than 1 microsecond
79 | 			humanReadable = fmt.Sprintf("%d nanoseconds", durationNs)
80 | 		case durationNs < 1000000: // Less than 1 millisecond
81 | 			humanReadable = fmt.Sprintf("%.2f microseconds", float64(durationNs)/1000)
82 | 		case durationNs < 1000000000: // Less than 1 second
83 | 			humanReadable = fmt.Sprintf("%.2f milliseconds", float64(durationNs)/1000000)
84 | 		case durationNs < 60000000000: // Less than 1 minute
85 | 			humanReadable = fmt.Sprintf("%.2f seconds", float64(durationNs)/1000000000)
86 | 		default: // 1 minute or more
87 | 			minutes := durationNs / 60000000000
88 | 			seconds := (durationNs % 60000000000) / 1000000000
89 | 			humanReadable = fmt.Sprintf("%d minutes %.2f seconds", minutes, float64(seconds))
90 | 		}
91 | 
92 | 		// Add human readable duration to span attributes
93 | 		trace.DurationReadable = humanReadable
94 | 	}
95 | 
96 | 	return json.Marshal(tracesResponse)
97 | }
98 | 
```

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

```go
  1 | package tools
  2 | 
  3 | import (
  4 | 	"bytes"
  5 | 	"context"
  6 | 	"encoding/json"
  7 | 	"fmt"
  8 | 
  9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
 10 | 	"github.com/metoro-io/metoro-mcp-server/model"
 11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
 12 | 	"gopkg.in/yaml.v3"
 13 | )
 14 | 
 15 | type GetVersionForServiceHandlerArgs struct {
 16 | 	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"`
 17 | 	ServiceName  string           `json:"serviceName" jsonschema:"required,description=The name of the service to get container versions for."`
 18 | 	Environments []string         `json:"environments" jsonschema:"description=The environments to get service versions for. If empty all environments will be used."`
 19 | }
 20 | 
 21 | type GetVersionForServiceResponse struct {
 22 | 	ContainerVersions map[string]map[string]string `json:"container_versions"`
 23 | }
 24 | 
 25 | type K8sResourceSummaryResponse struct {
 26 | 	K8sResourceSummary []struct {
 27 | 		Environment  string `json:"environment"`
 28 | 		Kind         string `json:"kind"`
 29 | 		ResourceYaml string `json:"resourceYaml"`
 30 | 	} `json:"k8sResourceSummary"`
 31 | }
 32 | 
 33 | func GetVersionForServiceHandler(ctx context.Context, arguments GetVersionForServiceHandlerArgs) (*mcpgolang.ToolResponse, error) {
 34 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
 35 | 	if err != nil {
 36 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
 37 | 	}
 38 | 	request := model.GetPodsRequest{
 39 | 		StartTime:    startTime,
 40 | 		EndTime:      endTime,
 41 | 		ServiceName:  arguments.ServiceName,
 42 | 		Environments: arguments.Environments,
 43 | 	}
 44 | 	jsonBody, err := json.Marshal(request)
 45 | 	if err != nil {
 46 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
 47 | 	}
 48 | 
 49 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "k8s/summary", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
 50 | 	if err != nil {
 51 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
 52 | 	}
 53 | 
 54 | 	// Parse the JSON response
 55 | 	var summaryResponse K8sResourceSummaryResponse
 56 | 	err = json.Unmarshal(resp, &summaryResponse)
 57 | 	if err != nil {
 58 | 		return nil, fmt.Errorf("error parsing JSON response: %v", err)
 59 | 	}
 60 | 
 61 | 	// Extract container versions from each environment
 62 | 	containerVersions := make(map[string]map[string]string)
 63 | 
 64 | 	for _, resource := range summaryResponse.K8sResourceSummary {
 65 | 		// Parse the YAML for each resource
 66 | 		var yamlData map[string]interface{}
 67 | 		err = yaml.Unmarshal([]byte(resource.ResourceYaml), &yamlData)
 68 | 		if err != nil {
 69 | 			continue // Skip if we can't parse this resource
 70 | 		}
 71 | 
 72 | 		// Extract containers for this environment
 73 | 		envContainers := make(map[string]string)
 74 | 
 75 | 		// Check for spec.template.spec.containers (Deployment/StatefulSet)
 76 | 		if spec, ok := yamlData["spec"].(map[string]interface{}); ok {
 77 | 			if template, ok := spec["template"].(map[string]interface{}); ok {
 78 | 				if templateSpec, ok := template["spec"].(map[string]interface{}); ok {
 79 | 					extractContainers(templateSpec, envContainers)
 80 | 				}
 81 | 			}
 82 | 			// Also check spec.containers directly (DaemonSet)
 83 | 			extractContainers(spec, envContainers)
 84 | 		}
 85 | 
 86 | 		if len(envContainers) > 0 {
 87 | 			containerVersions[resource.Environment] = envContainers
 88 | 		}
 89 | 	}
 90 | 
 91 | 	response := GetVersionForServiceResponse{
 92 | 		ContainerVersions: containerVersions,
 93 | 	}
 94 | 
 95 | 	jsonResponse, err := json.Marshal(response)
 96 | 	if err != nil {
 97 | 		return nil, fmt.Errorf("error marshaling response: %v", err)
 98 | 	}
 99 | 
100 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(string(jsonResponse))), nil
101 | }
102 | 
103 | func extractContainers(spec map[string]interface{}, containerVersions map[string]string) {
104 | 	if containers, ok := spec["containers"].([]interface{}); ok {
105 | 		for _, container := range containers {
106 | 			if containerMap, ok := container.(map[string]interface{}); ok {
107 | 				containerName, nameOk := containerMap["name"].(string)
108 | 				image, imageOk := containerMap["image"].(string)
109 | 				if nameOk && imageOk {
110 | 					containerVersions[containerName] = image
111 | 				}
112 | 			}
113 | 		}
114 | 	}
115 | 
116 | 	// Also check for init containers
117 | 	if initContainers, ok := spec["initContainers"].([]interface{}); ok {
118 | 		for _, container := range initContainers {
119 | 			if containerMap, ok := container.(map[string]interface{}); ok {
120 | 				containerName, nameOk := containerMap["name"].(string)
121 | 				image, imageOk := containerMap["image"].(string)
122 | 				if nameOk && imageOk {
123 | 					containerVersions["init-"+containerName] = image
124 | 				}
125 | 			}
126 | 		}
127 | 	}
128 | }
129 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetTraceMetricHandlerArgs struct {
15 | 	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"`
16 | 	ServiceNames   []string               `json:"serviceNames" jsonschema:"description=Service names to return the trace timeseries data for"`
17 | 	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"`
18 | 	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"`
19 | 	Regexes        []string               `json:"regexes" jsonschema:"description=The regexes to apply to the trace's endpoints. Traces with endpoints matching regexes will be returned"`
20 | 	ExcludeRegexes []string               `json:"excludeRegexes" jsonschema:"description=The regexes to exclude from the trace's endpoints. Traces with endpoints matching regexes will be excluded"`
21 | 	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"`
22 | 	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"`
23 | 	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"`
24 | 	Environments   []string               `json:"environments" jsonschema:"description=The environments to get traces from. If empty traces from all environments will be returned"`
25 | }
26 | 
27 | func GetTraceMetricHandler(ctx context.Context, arguments GetTraceMetricHandlerArgs) (*mcpgolang.ToolResponse, error) {
28 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
29 | 	if err != nil {
30 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
31 | 	}
32 | 	request := model.GetTraceMetricRequest{
33 | 		StartTime:      startTime,
34 | 		EndTime:        endTime,
35 | 		ServiceNames:   arguments.ServiceNames,
36 | 		Filters:        arguments.Filters,
37 | 		ExcludeFilters: arguments.ExcludeFilters,
38 | 		Regexes:        arguments.Regexes,
39 | 		ExcludeRegexes: arguments.ExcludeRegexes,
40 | 		Splits:         arguments.Splits,
41 | 		Functions:      arguments.Functions,
42 | 		Aggregate:      model.Aggregation(arguments.Aggregate),
43 | 		Environments:   arguments.Environments,
44 | 	}
45 | 
46 | 	body, err := getTraceMetricMetoroCall(ctx, request)
47 | 	if err != nil {
48 | 		return nil, fmt.Errorf("error getting trace metric: %v", err)
49 | 	}
50 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(body)))), nil
51 | }
52 | 
53 | func getTraceMetricMetoroCall(ctx context.Context, request model.GetTraceMetricRequest) ([]byte, error) {
54 | 	jsonBody, err := json.Marshal(request)
55 | 	if err != nil {
56 | 		return nil, fmt.Errorf("error marshaling request: %v", err)
57 | 	}
58 | 
59 | 	resp, err := utils.MakeMetoroAPIRequest("POST", "traceMetric", bytes.NewBuffer(jsonBody), utils.GetAPIRequirementsFromRequest(ctx))
60 | 	if err != nil {
61 | 		return nil, fmt.Errorf("error making Metoro call: %v", err)
62 | 	}
63 | 
64 | 	return resp, nil
65 | }
66 | 
```

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

```go
 1 | package tools
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"context"
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 
 9 | 	mcpgolang "github.com/metoro-io/mcp-golang"
10 | 	"github.com/metoro-io/metoro-mcp-server/model"
11 | 	"github.com/metoro-io/metoro-mcp-server/utils"
12 | )
13 | 
14 | type GetLogsHandlerArgs struct {
15 | 	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"`
16 | 	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."`
17 | 	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."`
18 | 	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"`
19 | 	Environments   []string            `json:"environments" jsonschema:"description=The environments to get logs from. If empty logs from all environments will be returned"`
20 | }
21 | 
22 | func GetLogsHandler(ctx context.Context, arguments GetLogsHandlerArgs) (*mcpgolang.ToolResponse, error) {
23 | 	startTime, endTime, err := utils.CalculateTimeRange(arguments.TimeConfig)
24 | 	if err != nil {
25 | 		return nil, fmt.Errorf("error calculating time range: %v", err)
26 | 	}
27 | 
28 | 	var regexes = []string{}
29 | 	if arguments.Regex != "" {
30 | 		regexes = append(regexes, arguments.Regex)
31 | 	}
32 | 
33 | 	err = CheckAttributes(ctx, model.Logs, arguments.Filters, arguments.ExcludeFilters, []string{}, nil)
34 | 	if err != nil {
35 | 		return nil, err
36 | 	}
37 | 	limit := 20
38 | 
39 | 	request := model.GetLogsRequest{
40 | 		StartTime:      startTime,
41 | 		EndTime:        endTime,
42 | 		Filters:        arguments.Filters,
43 | 		ExcludeFilters: arguments.ExcludeFilters,
44 | 		Regexes:        regexes,
45 | 		Environments:   arguments.Environments,
46 | 		ExportLimit:    &limit,
47 | 	}
48 | 
49 | 	resp, err := getLogsMetoroCall(ctx, request)
50 | 	if err != nil {
51 | 		return nil, fmt.Errorf("error getting logs: %v", err)
52 | 	}
53 | 	respTrimmed, err := trimLogsResponse(resp)
54 | 	if err != nil {
55 | 		return nil, fmt.Errorf("error trimming logs: %v", err)
56 | 	}
57 | 
58 | 	return mcpgolang.NewToolResponse(mcpgolang.NewTextContent(fmt.Sprintf("%s", string(respTrimmed)))), nil
59 | }
60 | 
61 | func getLogsMetoroCall(ctx context.Context, request model.GetLogsRequest) ([]byte, error) {
62 | 	requestBody, err := json.Marshal(request)
63 | 	if err != nil {
64 | 		return nil, fmt.Errorf("error marshaling logs request: %v", err)
65 | 	}
66 | 	return utils.MakeMetoroAPIRequest("POST", "logs", bytes.NewBuffer(requestBody), utils.GetAPIRequirementsFromRequest(ctx))
67 | }
68 | 
69 | func trimLogsResponse(response []byte) ([]byte, error) {
70 | 	var logsResponse model.GetLogsResponse
71 | 	err := json.Unmarshal(response, &logsResponse)
72 | 	if err != nil {
73 | 		return nil, fmt.Errorf("error unmarshaling logs response: %v", err)
74 | 	}
75 | 
76 | 	// Trim every log entry to only include the first 2000 characters of the message
77 | 	// This is to prevent excessively long log messages from blowing up the context.
78 | 	logLineLengthLimit := 2000
79 | 	for i := range logsResponse.Logs {
80 | 		if len(logsResponse.Logs[i].Message) > logLineLengthLimit {
81 | 			logsResponse.Logs[i].Message = logsResponse.Logs[i].Message[:logLineLengthLimit]
82 | 		}
83 | 	}
84 | 
85 | 	return json.Marshal(logsResponse)
86 | }
87 | 
```
Page 1/3FirstPrevNextLast