mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2025-09-20 18:58:47 +00:00
fix: migrated to more actively maintained mcp golang lib and added AI explain (#1557)
* migrated to more actively maintained mcp golang lib and added AI explain support for mcp mode Signed-off-by: Umesh Kaul <umeshkaul@gmail.com> * added a makefile option to create local docker image for testing Signed-off-by: Umesh Kaul <umeshkaul@gmail.com> * fixed linter errors and made anonymize as an arg Signed-off-by: Umesh Kaul <umeshkaul@gmail.com> --------- Signed-off-by: Umesh Kaul <umeshkaul@gmail.com> Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
This commit is contained in:
6
Makefile
6
Makefile
@@ -85,6 +85,12 @@ docker-build:
|
|||||||
@echo "===========> Building docker image"
|
@echo "===========> Building docker image"
|
||||||
docker buildx build --build-arg=VERSION="$$(git describe --tags --abbrev=0)" --build-arg=COMMIT="$$(git rev-parse --short HEAD)" --build-arg DATE="$$(date +%FT%TZ)" --platform="linux/amd64,linux/arm64" -t ${IMG} -f container/Dockerfile . --push
|
docker buildx build --build-arg=VERSION="$$(git describe --tags --abbrev=0)" --build-arg=COMMIT="$$(git rev-parse --short HEAD)" --build-arg DATE="$$(date +%FT%TZ)" --platform="linux/amd64,linux/arm64" -t ${IMG} -f container/Dockerfile . --push
|
||||||
|
|
||||||
|
## docker-build-local: Build docker image for local testing
|
||||||
|
.PHONY: docker-build-local
|
||||||
|
docker-build-local:
|
||||||
|
@echo "===========> Building docker image for local testing"
|
||||||
|
docker build --build-arg=VERSION="$$(git describe --tags --abbrev=0)" --build-arg=COMMIT="$$(git rev-parse --short HEAD)" --build-arg DATE="$$(date +%FT%TZ)" -t k8sgpt:local -f container/Dockerfile .
|
||||||
|
|
||||||
## fmt: Run go fmt against code.
|
## fmt: Run go fmt against code.
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
|
29
go.mod
29
go.mod
@@ -47,14 +47,13 @@ require (
|
|||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1
|
||||||
github.com/hupe1980/go-huggingface v0.0.15
|
github.com/hupe1980/go-huggingface v0.0.15
|
||||||
github.com/kyverno/policy-reporter-kyverno-plugin v1.6.4
|
github.com/kyverno/policy-reporter-kyverno-plugin v1.6.4
|
||||||
github.com/metoro-io/mcp-golang v0.11.0
|
github.com/mark3labs/mcp-go v0.36.0
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.79.0
|
github.com/oracle/oci-go-sdk/v65 v65.79.0
|
||||||
github.com/prometheus/prometheus v0.302.1
|
github.com/prometheus/prometheus v0.302.1
|
||||||
github.com/pterm/pterm v0.12.80
|
github.com/pterm/pterm v0.12.80
|
||||||
google.golang.org/api v0.218.0
|
google.golang.org/api v0.218.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
|
||||||
sigs.k8s.io/controller-runtime v0.19.3
|
sigs.k8s.io/controller-runtime v0.19.3
|
||||||
sigs.k8s.io/gateway-api v1.2.1
|
sigs.k8s.io/gateway-api v1.2.1
|
||||||
)
|
)
|
||||||
@@ -97,11 +96,7 @@ require (
|
|||||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
github.com/buger/jsonparser v1.1.1 // indirect
|
github.com/buger/jsonparser v1.1.1 // indirect
|
||||||
github.com/bytedance/sonic v1.11.6 // indirect
|
|
||||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
|
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
|
||||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
|
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
|
||||||
github.com/containerd/console v1.0.4 // indirect
|
github.com/containerd/console v1.0.4 // indirect
|
||||||
github.com/containerd/continuity v0.4.3 // indirect
|
github.com/containerd/continuity v0.4.3 // indirect
|
||||||
@@ -117,13 +112,6 @@ require (
|
|||||||
github.com/expr-lang/expr v1.17.2 // indirect
|
github.com/expr-lang/expr v1.17.2 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
|
||||||
github.com/gin-gonic/gin v1.10.0 // indirect
|
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
|
||||||
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
|
||||||
github.com/gofrs/flock v0.12.1 // indirect
|
github.com/gofrs/flock v0.12.1 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
@@ -134,12 +122,10 @@ require (
|
|||||||
github.com/gookit/color v1.5.4 // indirect
|
github.com/gookit/color v1.5.4 // indirect
|
||||||
github.com/gorilla/websocket v1.5.1 // indirect
|
github.com/gorilla/websocket v1.5.1 // indirect
|
||||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
|
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
|
||||||
github.com/invopop/jsonschema v0.12.0 // indirect
|
github.com/invopop/jsonschema v0.13.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/jpillora/backoff v1.0.0 // indirect
|
github.com/jpillora/backoff v1.0.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
|
||||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||||
github.com/moby/sys/mountinfo v0.7.1 // indirect
|
github.com/moby/sys/mountinfo v0.7.1 // indirect
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||||
@@ -154,15 +140,10 @@ require (
|
|||||||
github.com/sony/gobreaker v0.5.0 // indirect
|
github.com/sony/gobreaker v0.5.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/tidwall/gjson v1.18.0 // indirect
|
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
|
||||||
github.com/tidwall/sjson v1.2.5 // indirect
|
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
|
||||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
|
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect
|
go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect
|
||||||
@@ -171,12 +152,12 @@ require (
|
|||||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect
|
go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect
|
||||||
golang.org/x/arch v0.8.0 // indirect
|
|
||||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
knative.dev/pkg v0.0.0-20241026180704-25f6002b00f3 // indirect
|
knative.dev/pkg v0.0.0-20241026180704-25f6002b00f3 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -271,7 +252,7 @@ require (
|
|||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/cast v1.7.0 // indirect
|
github.com/spf13/cast v1.7.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
|
59
go.sum
59
go.sum
@@ -789,10 +789,6 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj
|
|||||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
|
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
|
||||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
|
||||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
|
||||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
|
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
|
||||||
@@ -810,10 +806,6 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
|||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
|
||||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
|
||||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
@@ -926,13 +918,7 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/
|
|||||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
|
||||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
|
||||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
|
||||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||||
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
|
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
|
||||||
@@ -965,14 +951,6 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
|
|||||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||||
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
|
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
|
||||||
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
|
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
|
||||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
|
||||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
|
||||||
github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E=
|
github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E=
|
||||||
github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
@@ -985,8 +963,6 @@ github.com/go-zookeeper/zk v1.0.4/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL
|
|||||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
|
||||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||||
@@ -1192,8 +1168,8 @@ github.com/imdario/mergo v1.0.1 h1:lFIgOs30GMaV/2+qQ+eEBLbUL6h1YosdohE3ODy4hTs=
|
|||||||
github.com/imdario/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
github.com/imdario/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
|
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||||
github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||||
github.com/ionos-cloud/sdk-go/v6 v6.3.2 h1:2mUmrZZz6cPyT9IRX0T8fBLc/7XU/eTxP2Y5tS7/09k=
|
github.com/ionos-cloud/sdk-go/v6 v6.3.2 h1:2mUmrZZz6cPyT9IRX0T8fBLc/7XU/eTxP2Y5tS7/09k=
|
||||||
github.com/ionos-cloud/sdk-go/v6 v6.3.2/go.mod h1:SXrO9OGyWjd2rZhAhEpdYN6VUAODzzqRdqA9BCviQtI=
|
github.com/ionos-cloud/sdk-go/v6 v6.3.2/go.mod h1:SXrO9OGyWjd2rZhAhEpdYN6VUAODzzqRdqA9BCviQtI=
|
||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
@@ -1232,7 +1208,6 @@ github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuOb
|
|||||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
|
||||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=
|
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=
|
||||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
|
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
@@ -1255,8 +1230,6 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq
|
|||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
|
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
|
||||||
@@ -1271,6 +1244,8 @@ github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a
|
|||||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mark3labs/mcp-go v0.36.0 h1:rIZaijrRYPeSbJG8/qNDe0hWlGrCJ7FWHNMz2SQpTis=
|
||||||
|
github.com/mark3labs/mcp-go v0.36.0/go.mod h1:T7tUa2jO6MavG+3P25Oy/jR7iCeJPHImCZHRymCn39g=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
@@ -1285,8 +1260,6 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4
|
|||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/metoro-io/mcp-golang v0.11.0 h1:1k+VSE9QaeMTLn0gJ3FgE/DcjsCBsLFnz5eSFbgXUiI=
|
|
||||||
github.com/metoro-io/mcp-golang v0.11.0/go.mod h1:ifLP9ZzKpN1UqFWNTpAHOqSvNkMK6b7d1FSZ5Lu0lN0=
|
|
||||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||||
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
|
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
|
||||||
@@ -1460,8 +1433,8 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z
|
|||||||
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
@@ -1490,20 +1463,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
|
||||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
|
||||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
|
||||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
|
||||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
|
||||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
|
||||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
|
||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
|
||||||
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
|
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
|
||||||
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
|
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
|
||||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||||
@@ -1522,6 +1481,8 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd
|
|||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
|
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||||
|
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@@ -1576,9 +1537,6 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
|||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
|
||||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
|
||||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@@ -2328,7 +2286,6 @@ modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw
|
|||||||
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
|
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
|
||||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
|
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
|
||||||
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
|
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
|
||||||
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo=
|
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
@@ -40,12 +40,20 @@ type AnalyzeRequest struct {
|
|||||||
WithStats bool `json:"withStats,omitempty"`
|
WithStats bool `json:"withStats,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnalyzeResponse represents the output of the analyze tool
|
// JSONRPCResponse represents the JSON-RPC response format
|
||||||
type AnalyzeResponse struct {
|
type JSONRPCResponse struct {
|
||||||
|
JSONRPC string `json:"jsonrpc"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Result struct {
|
||||||
Content []struct {
|
Content []struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
} `json:"content"`
|
} `json:"content"`
|
||||||
|
} `json:"result,omitempty"`
|
||||||
|
Error *struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -65,23 +73,89 @@ func main() {
|
|||||||
MaxConcurrency: 10,
|
MaxConcurrency: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert request to JSON
|
// Note: req is now used directly in the JSON-RPC request
|
||||||
reqJSON, err := json.Marshal(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to marshal request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create HTTP client with timeout
|
// Create HTTP client with timeout
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: 5 * time.Minute,
|
Timeout: 5 * time.Minute,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send request to MCP server
|
// First, initialize the session
|
||||||
resp, err := client.Post(
|
initRequest := map[string]interface{}{
|
||||||
fmt.Sprintf("http://localhost:%s/mcp/analyze", *serverPort),
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "initialize",
|
||||||
|
"params": map[string]interface{}{
|
||||||
|
"protocolVersion": "2025-03-26",
|
||||||
|
"capabilities": map[string]interface{}{
|
||||||
|
"tools": map[string]interface{}{},
|
||||||
|
"resources": map[string]interface{}{},
|
||||||
|
"prompts": map[string]interface{}{},
|
||||||
|
},
|
||||||
|
"clientInfo": map[string]interface{}{
|
||||||
|
"name": "k8sgpt-client",
|
||||||
|
"version": "1.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
initData, err := json.Marshal(initRequest)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to marshal init request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send initialization request
|
||||||
|
initResp, err := client.Post(
|
||||||
|
fmt.Sprintf("http://localhost:%s/mcp", *serverPort),
|
||||||
"application/json",
|
"application/json",
|
||||||
bytes.NewBuffer(reqJSON),
|
bytes.NewBuffer(initData),
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to send init request: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := initResp.Body.Close(); err != nil {
|
||||||
|
log.Printf("Error closing init response body: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Extract session ID from response headers
|
||||||
|
sessionID := initResp.Header.Get("Mcp-Session-Id")
|
||||||
|
if sessionID == "" {
|
||||||
|
log.Println("Warning: No session ID received from server")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create JSON-RPC request for analyze
|
||||||
|
jsonRPCRequest := map[string]interface{}{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 2,
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": map[string]interface{}{
|
||||||
|
"name": "analyze",
|
||||||
|
"arguments": req,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to JSON
|
||||||
|
jsonRPCData, err := json.Marshal(jsonRPCRequest)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to marshal JSON-RPC request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create request with session ID if available
|
||||||
|
httpReq, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%s/mcp", *serverPort), bytes.NewBuffer(jsonRPCData))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpReq.Header.Set("Content-Type", "application/json")
|
||||||
|
httpReq.Header.Set("Accept", "application/json,text/event-stream")
|
||||||
|
if sessionID != "" {
|
||||||
|
httpReq.Header.Set("Mcp-Session-Id", sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send request to MCP server
|
||||||
|
resp, err := client.Do(httpReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to send request: %v", err)
|
log.Fatalf("Failed to send request: %v", err)
|
||||||
}
|
}
|
||||||
@@ -99,15 +173,17 @@ func main() {
|
|||||||
fmt.Printf("Raw response: %s\n", string(body))
|
fmt.Printf("Raw response: %s\n", string(body))
|
||||||
|
|
||||||
// Parse response
|
// Parse response
|
||||||
var analyzeResp AnalyzeResponse
|
var jsonRPCResp JSONRPCResponse
|
||||||
if err := json.Unmarshal(body, &analyzeResp); err != nil {
|
if err := json.Unmarshal(body, &jsonRPCResp); err != nil {
|
||||||
log.Fatalf("Failed to decode response: %v", err)
|
log.Fatalf("Failed to decode response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print results
|
// Print results
|
||||||
fmt.Println("Analysis Results:")
|
fmt.Println("Analysis Results:")
|
||||||
if len(analyzeResp.Content) > 0 {
|
if jsonRPCResp.Error != nil {
|
||||||
fmt.Println(analyzeResp.Content[0].Text)
|
fmt.Printf("Error: %s (code: %d)\n", jsonRPCResp.Error.Message, jsonRPCResp.Error.Code)
|
||||||
|
} else if len(jsonRPCResp.Result.Content) > 0 {
|
||||||
|
fmt.Println(jsonRPCResp.Result.Content[0].Text)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("No results returned")
|
fmt.Println("No results returned")
|
||||||
}
|
}
|
||||||
|
@@ -17,88 +17,205 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
schemav1 "buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go/schema/v1"
|
schemav1 "buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go/schema/v1"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/server/config"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/server/config"
|
||||||
mcp_golang "github.com/metoro-io/mcp-golang"
|
"github.com/mark3labs/mcp-go/mcp"
|
||||||
mcp_http "github.com/metoro-io/mcp-golang/transport/http"
|
"github.com/mark3labs/mcp-go/server"
|
||||||
"github.com/metoro-io/mcp-golang/transport/stdio"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MCPServer represents an MCP server for k8sgpt
|
// K8sGptMCPServer represents an MCP server for k8sgpt
|
||||||
type MCPServer struct {
|
type K8sGptMCPServer struct {
|
||||||
server *mcp_golang.Server
|
server *server.MCPServer
|
||||||
port string
|
port string
|
||||||
aiProvider *ai.AIProvider
|
aiProvider *ai.AIProvider
|
||||||
useHTTP bool
|
useHTTP bool
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
|
httpServer *server.StreamableHTTPServer
|
||||||
|
stdioServer *server.StdioServer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMCPServer creates a new MCP server
|
func NewMCPServer(port string, aiProvider *ai.AIProvider, useHTTP bool, logger *zap.Logger) (*K8sGptMCPServer, error) {
|
||||||
func NewMCPServer(port string, aiProvider *ai.AIProvider, useHTTP bool, logger *zap.Logger) (*MCPServer, error) {
|
opts := []server.ServerOption{
|
||||||
opts := []mcp_golang.ServerOptions{
|
server.WithToolCapabilities(true),
|
||||||
mcp_golang.WithName("k8sgpt"),
|
server.WithResourceCapabilities(true, false),
|
||||||
mcp_golang.WithVersion("1.0.0"),
|
server.WithPromptCapabilities(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
var server *mcp_golang.Server
|
// Create the MCP server
|
||||||
if useHTTP {
|
mcpServer := server.NewMCPServer("k8sgpt", "1.0.0", opts...)
|
||||||
logger.Info("starting MCP server with http transport on port", zap.String("port", port))
|
var k8sGptMCPServer = &K8sGptMCPServer{
|
||||||
httpTransport := mcp_http.NewHTTPTransport("/mcp").WithAddr(":" + port)
|
server: mcpServer,
|
||||||
server = mcp_golang.NewServer(httpTransport, opts...)
|
|
||||||
} else {
|
|
||||||
server = mcp_golang.NewServer(stdio.NewStdioServerTransport(), opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &MCPServer{
|
|
||||||
server: server,
|
|
||||||
port: port,
|
port: port,
|
||||||
aiProvider: aiProvider,
|
aiProvider: aiProvider,
|
||||||
useHTTP: useHTTP,
|
useHTTP: useHTTP,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register tools and resources immediately
|
||||||
|
if err := k8sGptMCPServer.registerToolsAndResources(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to register tools and resources: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if useHTTP {
|
||||||
|
// Create HTTP server with streamable transport
|
||||||
|
httpOpts := []server.StreamableHTTPOption{
|
||||||
|
server.WithLogger(&zapLoggerAdapter{logger: logger}),
|
||||||
|
}
|
||||||
|
|
||||||
|
httpServer := server.NewStreamableHTTPServer(mcpServer, httpOpts...)
|
||||||
|
|
||||||
|
// Launch the HTTP server directly
|
||||||
|
go func() {
|
||||||
|
logger.Info("Starting MCP HTTP server", zap.String("port", port))
|
||||||
|
if err := httpServer.Start(":" + port); err != nil {
|
||||||
|
logger.Fatal("MCP HTTP server failed", zap.Error(err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &K8sGptMCPServer{
|
||||||
|
server: mcpServer,
|
||||||
|
port: port,
|
||||||
|
aiProvider: aiProvider,
|
||||||
|
useHTTP: useHTTP,
|
||||||
|
logger: logger,
|
||||||
|
httpServer: httpServer,
|
||||||
}, nil
|
}, nil
|
||||||
|
} else {
|
||||||
|
// Create stdio server
|
||||||
|
stdioServer := server.NewStdioServer(mcpServer)
|
||||||
|
|
||||||
|
return &K8sGptMCPServer{
|
||||||
|
server: mcpServer,
|
||||||
|
port: port,
|
||||||
|
aiProvider: aiProvider,
|
||||||
|
useHTTP: useHTTP,
|
||||||
|
logger: logger,
|
||||||
|
stdioServer: stdioServer,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the MCP server
|
// Start starts the MCP server
|
||||||
func (s *MCPServer) Start() error {
|
func (s *K8sGptMCPServer) Start() error {
|
||||||
if s.server == nil {
|
if s.server == nil {
|
||||||
return fmt.Errorf("server not initialized")
|
return fmt.Errorf("server not initialized")
|
||||||
}
|
}
|
||||||
|
// Register prompts
|
||||||
// Register analyze tool
|
if err := s.registerPrompts(); err != nil {
|
||||||
if err := s.server.RegisterTool("analyze", "Analyze Kubernetes resources", s.handleAnalyze); err != nil {
|
return fmt.Errorf("failed to register prompts: %v", err)
|
||||||
return fmt.Errorf("failed to register analyze tool: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register cluster info tool
|
|
||||||
if err := s.server.RegisterTool("cluster-info", "Get Kubernetes cluster information", s.handleClusterInfo); err != nil {
|
|
||||||
return fmt.Errorf("failed to register cluster-info tool: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register config tool
|
|
||||||
if err := s.server.RegisterTool("config", "Configure K8sGPT settings", s.handleConfig); err != nil {
|
|
||||||
return fmt.Errorf("failed to register config tool: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register resources
|
// Register resources
|
||||||
if err := s.registerResources(); err != nil {
|
if err := s.registerResources(); err != nil {
|
||||||
return fmt.Errorf("failed to register resources: %v", err)
|
return fmt.Errorf("failed to register resources: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register prompts
|
// Start the server based on transport type
|
||||||
if err := s.registerPrompts(); err != nil {
|
if s.useHTTP {
|
||||||
return fmt.Errorf("failed to register prompts: %v", err)
|
// HTTP server is already running in a goroutine
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
// Start stdio server (this will block)
|
||||||
|
return server.ServeStdio(s.server)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start the server (this will block)
|
func (s *K8sGptMCPServer) registerToolsAndResources() error {
|
||||||
if err := s.server.Serve(); err != nil {
|
// Register analyze tool with proper JSON schema
|
||||||
s.logger.Error("Error starting MCP server", zap.Error(err))
|
analyzeTool := mcp.NewTool("analyze",
|
||||||
}
|
mcp.WithDescription("Analyze Kubernetes resources for issues and problems"),
|
||||||
|
mcp.WithString("namespace",
|
||||||
|
mcp.Description("Kubernetes namespace to analyze (empty for all namespaces)"),
|
||||||
|
),
|
||||||
|
mcp.WithString("backend",
|
||||||
|
mcp.Description("AI backend to use for analysis (e.g., openai, azure, localai)"),
|
||||||
|
),
|
||||||
|
mcp.WithBoolean("explain",
|
||||||
|
mcp.Description("Provide detailed explanations for issues"),
|
||||||
|
),
|
||||||
|
mcp.WithArray("filters",
|
||||||
|
mcp.Description("Provide filters to narrow down the analysis (e.g. ['Pods', 'Deployments'])"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
s.server.AddTool(analyzeTool, s.handleAnalyze)
|
||||||
|
|
||||||
|
// Register cluster info tool (no parameters needed)
|
||||||
|
clusterInfoTool := mcp.NewTool("cluster-info",
|
||||||
|
mcp.WithDescription("Get Kubernetes cluster information and version"),
|
||||||
|
)
|
||||||
|
s.server.AddTool(clusterInfoTool, s.handleClusterInfo)
|
||||||
|
|
||||||
|
// Register config tool with proper JSON schema
|
||||||
|
configTool := mcp.NewTool("config",
|
||||||
|
mcp.WithDescription("Configure K8sGPT settings including custom analyzers and cache"),
|
||||||
|
mcp.WithObject("customAnalyzers",
|
||||||
|
mcp.Description("Custom analyzer configurations"),
|
||||||
|
mcp.Properties(map[string]any{
|
||||||
|
"name": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name of the custom analyzer",
|
||||||
|
},
|
||||||
|
"connection": map[string]any{
|
||||||
|
"type": "object",
|
||||||
|
"properties": map[string]any{
|
||||||
|
"url": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "URL of the custom analyzer service",
|
||||||
|
},
|
||||||
|
"port": map[string]any{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Port of the custom analyzer service",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
mcp.WithObject("cache",
|
||||||
|
mcp.Description("Cache configuration"),
|
||||||
|
mcp.Properties(map[string]any{
|
||||||
|
"type": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Cache type (s3, azure, gcs)",
|
||||||
|
"enum": []string{"s3", "azure", "gcs"},
|
||||||
|
},
|
||||||
|
"bucketName": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Bucket name for S3/GCS cache",
|
||||||
|
},
|
||||||
|
"region": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Region for S3/GCS cache",
|
||||||
|
},
|
||||||
|
"endpoint": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Custom endpoint for S3 cache",
|
||||||
|
},
|
||||||
|
"insecure": map[string]any{
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Use insecure connection for cache",
|
||||||
|
},
|
||||||
|
"storageAccount": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Storage account for Azure cache",
|
||||||
|
},
|
||||||
|
"containerName": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Container name for Azure cache",
|
||||||
|
},
|
||||||
|
"projectId": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Project ID for GCS cache",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
s.server.AddTool(configTool, s.handleConfig)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +233,7 @@ type AnalyzeRequest struct {
|
|||||||
InteractiveMode bool `json:"interactiveMode,omitempty"`
|
InteractiveMode bool `json:"interactiveMode,omitempty"`
|
||||||
CustomHeaders []string `json:"customHeaders,omitempty"`
|
CustomHeaders []string `json:"customHeaders,omitempty"`
|
||||||
WithStats bool `json:"withStats,omitempty"`
|
WithStats bool `json:"withStats,omitempty"`
|
||||||
|
Anonymize bool `json:"anonymize,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnalyzeResponse represents the output of the analyze tool
|
// AnalyzeResponse represents the output of the analyze tool
|
||||||
@@ -163,62 +281,74 @@ type ConfigResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handleAnalyze handles the analyze tool
|
// handleAnalyze handles the analyze tool
|
||||||
func (s *MCPServer) handleAnalyze(ctx context.Context, request *AnalyzeRequest) (*mcp_golang.ToolResponse, error) {
|
func (s *K8sGptMCPServer) handleAnalyze(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
// Get stored configuration
|
|
||||||
var configAI ai.AIConfiguration
|
var req AnalyzeRequest
|
||||||
if err := viper.UnmarshalKey("ai", &configAI); err != nil {
|
if err := request.BindArguments(&req); err != nil {
|
||||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("Failed to load AI configuration: %v", err))), nil
|
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
|
||||||
}
|
}
|
||||||
// Use stored configuration if not specified in request
|
|
||||||
if request.Backend == "" {
|
if req.Backend == "" {
|
||||||
if configAI.DefaultProvider != "" {
|
if s.aiProvider.Name != "" {
|
||||||
request.Backend = configAI.DefaultProvider
|
req.Backend = s.aiProvider.Name
|
||||||
} else if len(configAI.Providers) > 0 {
|
|
||||||
request.Backend = configAI.Providers[0].Name
|
|
||||||
} else {
|
} else {
|
||||||
request.Backend = "openai" // fallback default
|
req.Backend = "openai" // fallback default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
request.Explain = true
|
|
||||||
// Get stored filters if not specified
|
// Get stored filters if not specified
|
||||||
if len(request.Filters) == 0 {
|
if len(req.Filters) == 0 {
|
||||||
request.Filters = viper.GetStringSlice("active_filters")
|
req.Filters = viper.GetStringSlice("active_filters")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate MaxConcurrency to prevent excessive memory allocation
|
// Validate MaxConcurrency to prevent excessive memory allocation
|
||||||
request.MaxConcurrency = validateMaxConcurrency(request.MaxConcurrency)
|
req.MaxConcurrency = validateMaxConcurrency(req.MaxConcurrency)
|
||||||
|
|
||||||
// Create a new analysis with the request parameters
|
// Create a new analysis with the request parameters
|
||||||
analysis, err := analysis.NewAnalysis(
|
analysis, err := analysis.NewAnalysis(
|
||||||
request.Backend,
|
req.Backend,
|
||||||
request.Language,
|
req.Language,
|
||||||
request.Filters,
|
req.Filters,
|
||||||
request.Namespace,
|
req.Namespace,
|
||||||
request.LabelSelector,
|
req.LabelSelector,
|
||||||
request.NoCache,
|
req.NoCache,
|
||||||
request.Explain,
|
req.Explain,
|
||||||
request.MaxConcurrency,
|
req.MaxConcurrency,
|
||||||
request.WithDoc,
|
req.WithDoc,
|
||||||
request.InteractiveMode,
|
req.InteractiveMode,
|
||||||
request.CustomHeaders,
|
req.CustomHeaders,
|
||||||
request.WithStats,
|
req.WithStats,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("Failed to create analysis: %v", err))), nil
|
return mcp.NewToolResultErrorf("Failed to create analysis: %v", err), nil
|
||||||
}
|
}
|
||||||
defer analysis.Close()
|
defer analysis.Close()
|
||||||
|
|
||||||
// Run the analysis
|
// Run the analysis
|
||||||
analysis.RunAnalysis()
|
analysis.RunAnalysis()
|
||||||
|
if req.Explain {
|
||||||
|
|
||||||
|
var output string
|
||||||
|
err := analysis.GetAIResults(output, req.Anonymize)
|
||||||
|
if err != nil {
|
||||||
|
return mcp.NewToolResultErrorf("Failed to get results from AI: %v", err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert results to JSON string using PrintOutput
|
||||||
|
outputBytes, err := analysis.PrintOutput("text")
|
||||||
|
if err != nil {
|
||||||
|
return mcp.NewToolResultErrorf("Failed to convert results to string: %v", err), nil
|
||||||
|
}
|
||||||
|
plainText := stripANSI(string(outputBytes))
|
||||||
|
return mcp.NewToolResultText(plainText), nil
|
||||||
|
} else {
|
||||||
// Get the output
|
// Get the output
|
||||||
output, err := analysis.PrintOutput("json")
|
output, err := analysis.PrintOutput("json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("Failed to print output: %v", err))), nil
|
return mcp.NewToolResultErrorf("Failed to print output: %v", err), nil
|
||||||
|
}
|
||||||
|
return mcp.NewToolResultText(string(output)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(string(output))), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateMaxConcurrency validates and bounds the MaxConcurrency parameter
|
// validateMaxConcurrency validates and bounds the MaxConcurrency parameter
|
||||||
@@ -233,25 +363,31 @@ func validateMaxConcurrency(maxConcurrency int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handleClusterInfo handles the cluster-info tool
|
// handleClusterInfo handles the cluster-info tool
|
||||||
func (s *MCPServer) handleClusterInfo(ctx context.Context, request *ClusterInfoRequest) (*mcp_golang.ToolResponse, error) {
|
func (s *K8sGptMCPServer) handleClusterInfo(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
// Create a new Kubernetes client
|
// Create a new Kubernetes client
|
||||||
client, err := kubernetes.NewClient("", "")
|
client, err := kubernetes.NewClient("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("failed to create Kubernetes client: %v", err))), nil
|
return mcp.NewToolResultErrorf("failed to create Kubernetes client: %v", err), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get cluster info from the client
|
// Get cluster info from the client
|
||||||
version, err := client.Client.Discovery().ServerVersion()
|
version, err := client.Client.Discovery().ServerVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("failed to get cluster version: %v", err))), nil
|
return mcp.NewToolResultErrorf("failed to get cluster version: %v", err), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
info := fmt.Sprintf("Kubernetes %s", version.GitVersion)
|
info := fmt.Sprintf("Kubernetes %s", version.GitVersion)
|
||||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(info)), nil
|
return mcp.NewToolResultText(info), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleConfig handles the config tool
|
// handleConfig handles the config tool
|
||||||
func (s *MCPServer) handleConfig(ctx context.Context, request *ConfigRequest) (*mcp_golang.ToolResponse, error) {
|
func (s *K8sGptMCPServer) handleConfig(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
|
// Parse request arguments
|
||||||
|
var req ConfigRequest
|
||||||
|
if err := request.BindArguments(&req); err != nil {
|
||||||
|
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new config handler
|
// Create a new config handler
|
||||||
handler := &config.Handler{}
|
handler := &config.Handler{}
|
||||||
|
|
||||||
@@ -261,8 +397,8 @@ func (s *MCPServer) handleConfig(ctx context.Context, request *ConfigRequest) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add custom analyzers if present
|
// Add custom analyzers if present
|
||||||
if len(request.CustomAnalyzers) > 0 {
|
if len(req.CustomAnalyzers) > 0 {
|
||||||
for _, ca := range request.CustomAnalyzers {
|
for _, ca := range req.CustomAnalyzers {
|
||||||
addConfigReq.CustomAnalyzers = append(addConfigReq.CustomAnalyzers, &schemav1.CustomAnalyzer{
|
addConfigReq.CustomAnalyzers = append(addConfigReq.CustomAnalyzers, &schemav1.CustomAnalyzer{
|
||||||
Name: ca.Name,
|
Name: ca.Name,
|
||||||
Connection: &schemav1.Connection{
|
Connection: &schemav1.Connection{
|
||||||
@@ -274,31 +410,31 @@ func (s *MCPServer) handleConfig(ctx context.Context, request *ConfigRequest) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add cache configuration if present
|
// Add cache configuration if present
|
||||||
if request.Cache.Type != "" {
|
if req.Cache.Type != "" {
|
||||||
cacheConfig := &schemav1.Cache{}
|
cacheConfig := &schemav1.Cache{}
|
||||||
switch request.Cache.Type {
|
switch req.Cache.Type {
|
||||||
case "s3":
|
case "s3":
|
||||||
cacheConfig.CacheType = &schemav1.Cache_S3Cache{
|
cacheConfig.CacheType = &schemav1.Cache_S3Cache{
|
||||||
S3Cache: &schemav1.S3Cache{
|
S3Cache: &schemav1.S3Cache{
|
||||||
BucketName: request.Cache.BucketName,
|
BucketName: req.Cache.BucketName,
|
||||||
Region: request.Cache.Region,
|
Region: req.Cache.Region,
|
||||||
Endpoint: request.Cache.Endpoint,
|
Endpoint: req.Cache.Endpoint,
|
||||||
Insecure: request.Cache.Insecure,
|
Insecure: req.Cache.Insecure,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case "azure":
|
case "azure":
|
||||||
cacheConfig.CacheType = &schemav1.Cache_AzureCache{
|
cacheConfig.CacheType = &schemav1.Cache_AzureCache{
|
||||||
AzureCache: &schemav1.AzureCache{
|
AzureCache: &schemav1.AzureCache{
|
||||||
StorageAccount: request.Cache.StorageAccount,
|
StorageAccount: req.Cache.StorageAccount,
|
||||||
ContainerName: request.Cache.ContainerName,
|
ContainerName: req.Cache.ContainerName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case "gcs":
|
case "gcs":
|
||||||
cacheConfig.CacheType = &schemav1.Cache_GcsCache{
|
cacheConfig.CacheType = &schemav1.Cache_GcsCache{
|
||||||
GcsCache: &schemav1.GCSCache{
|
GcsCache: &schemav1.GCSCache{
|
||||||
BucketName: request.Cache.BucketName,
|
BucketName: req.Cache.BucketName,
|
||||||
Region: request.Cache.Region,
|
Region: req.Cache.Region,
|
||||||
ProjectId: request.Cache.ProjectId,
|
ProjectId: req.Cache.ProjectId,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,27 +443,30 @@ func (s *MCPServer) handleConfig(ctx context.Context, request *ConfigRequest) (*
|
|||||||
|
|
||||||
// Apply the configuration using the shared function
|
// Apply the configuration using the shared function
|
||||||
if err := handler.ApplyConfig(ctx, addConfigReq); err != nil {
|
if err := handler.ApplyConfig(ctx, addConfigReq); err != nil {
|
||||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("Failed to add config: %v", err))), nil
|
return mcp.NewToolResultErrorf("Failed to add config: %v", err), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent("Successfully added configuration")), nil
|
return mcp.NewToolResultText("Successfully added configuration"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerPrompts registers the prompts for the MCP server
|
// registerPrompts registers the prompts for the MCP server
|
||||||
func (s *MCPServer) registerPrompts() error {
|
func (s *K8sGptMCPServer) registerPrompts() error {
|
||||||
// Register any prompts needed for the MCP server
|
// Register any prompts needed for the MCP server
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerResources registers the resources for the MCP server
|
// registerResources registers the resources for the MCP server
|
||||||
func (s *MCPServer) registerResources() error {
|
func (s *K8sGptMCPServer) registerResources() error {
|
||||||
if err := s.server.RegisterResource("cluster-info", "Get cluster information", "Get information about the Kubernetes cluster", "text", s.getClusterInfo); err != nil {
|
clusterInfoResource := mcp.NewResource("cluster-info", "cluster-info",
|
||||||
return fmt.Errorf("failed to register cluster-info resource: %v", err)
|
mcp.WithResourceDescription("Get information about the Kubernetes cluster"),
|
||||||
}
|
mcp.WithMIMEType("application/json"),
|
||||||
|
)
|
||||||
|
|
||||||
|
s.server.AddResource(clusterInfoResource, s.getClusterInfo)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MCPServer) getClusterInfo(ctx context.Context) (*mcp_golang.ResourceResponse, error) {
|
func (s *K8sGptMCPServer) getClusterInfo(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
|
||||||
// Create a new Kubernetes client
|
// Create a new Kubernetes client
|
||||||
client, err := kubernetes.NewClient("", "")
|
client, err := kubernetes.NewClient("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -346,24 +485,44 @@ func (s *MCPServer) getClusterInfo(ctx context.Context) (*mcp_golang.ResourceRes
|
|||||||
"gitVersion": version.GitVersion,
|
"gitVersion": version.GitVersion,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mcp_golang.NewResourceResponse(
|
return []mcp.ResourceContents{
|
||||||
mcp_golang.NewTextEmbeddedResource(
|
&mcp.TextResourceContents{
|
||||||
"cluster-info",
|
URI: "cluster-info",
|
||||||
"Failed to marshal cluster info",
|
MIMEType: "text/plain",
|
||||||
"text/plain",
|
Text: "Failed to marshal cluster info",
|
||||||
),
|
},
|
||||||
), nil
|
}, nil
|
||||||
}
|
}
|
||||||
return mcp_golang.NewResourceResponse(
|
|
||||||
mcp_golang.NewTextEmbeddedResource(
|
return []mcp.ResourceContents{
|
||||||
"cluster-info",
|
&mcp.TextResourceContents{
|
||||||
string(data),
|
URI: "cluster-info",
|
||||||
"application/json",
|
MIMEType: "application/json",
|
||||||
),
|
Text: string(data),
|
||||||
), nil
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the MCP server and releases resources
|
// Close closes the MCP server and releases resources
|
||||||
func (s *MCPServer) Close() error {
|
func (s *K8sGptMCPServer) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// zapLoggerAdapter adapts zap.Logger to the interface expected by mark3labs/mcp-go
|
||||||
|
type zapLoggerAdapter struct {
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *zapLoggerAdapter) Infof(format string, v ...any) {
|
||||||
|
z.logger.Info(fmt.Sprintf(format, v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *zapLoggerAdapter) Errorf(format string, v ...any) {
|
||||||
|
z.logger.Error(fmt.Sprintf(format, v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// stripANSI removes ANSI escape sequences from a string
|
||||||
|
func stripANSI(input string) string {
|
||||||
|
re := regexp.MustCompile(`\x1b\[[0-9;]*m`)
|
||||||
|
return re.ReplaceAllString(input, "")
|
||||||
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -78,14 +79,14 @@ func TestMCPServerCreation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test HTTP mode
|
// Test HTTP mode
|
||||||
mcpServer, err := NewMCPServer("8089", aiProvider, true, logger)
|
mcpServer, err := NewMCPServer("8088", aiProvider, true, logger)
|
||||||
assert.NoError(t, err, "Should be able to create MCP server with HTTP transport")
|
assert.NoError(t, err, "Should be able to create MCP server with HTTP transport")
|
||||||
assert.NotNil(t, mcpServer, "MCP server should not be nil")
|
assert.NotNil(t, mcpServer, "MCP server should not be nil")
|
||||||
assert.True(t, mcpServer.useHTTP, "MCP server should be in HTTP mode")
|
assert.True(t, mcpServer.useHTTP, "MCP server should be in HTTP mode")
|
||||||
assert.Equal(t, "8089", mcpServer.port, "Port should be set correctly")
|
assert.Equal(t, "8088", mcpServer.port, "Port should be set correctly")
|
||||||
|
|
||||||
// Test stdio mode
|
// Test stdio mode
|
||||||
mcpServerStdio, err := NewMCPServer("8089", aiProvider, false, logger)
|
mcpServerStdio, err := NewMCPServer("8088", aiProvider, false, logger)
|
||||||
assert.NoError(t, err, "Should be able to create MCP server with stdio transport")
|
assert.NoError(t, err, "Should be able to create MCP server with stdio transport")
|
||||||
assert.NotNil(t, mcpServerStdio, "MCP server should not be nil")
|
assert.NotNil(t, mcpServerStdio, "MCP server should not be nil")
|
||||||
assert.False(t, mcpServerStdio.useHTTP, "MCP server should be in stdio mode")
|
assert.False(t, mcpServerStdio.useHTTP, "MCP server should be in stdio mode")
|
||||||
@@ -107,27 +108,83 @@ func TestMCPServerBasicHTTP(t *testing.T) {
|
|||||||
Model: "test-model",
|
Model: "test-model",
|
||||||
}
|
}
|
||||||
|
|
||||||
mcpServer, err := NewMCPServer("8089", aiProvider, true, logger)
|
mcpServer, err := NewMCPServer("8091", aiProvider, true, logger)
|
||||||
assert.NoError(t, err, "Should be able to create MCP server")
|
assert.NoError(t, err, "Should be able to create MCP server")
|
||||||
|
|
||||||
// Start the MCP server in a goroutine
|
// For HTTP mode, the server is already started in NewMCPServer
|
||||||
go func() {
|
// No need to call Start() as it's already running in a goroutine
|
||||||
err := mcpServer.Start()
|
|
||||||
// Note: Start() might return an error when the server is stopped, which is expected
|
|
||||||
if err != nil {
|
|
||||||
logger.Info("MCP server stopped", zap.Error(err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Wait for the server to start
|
// Wait for the server to start
|
||||||
err = waitForPort("localhost:8089", 10*time.Second)
|
err = waitForPort("localhost:8091", 10*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Skipf("MCP server did not start within timeout: %v", err)
|
t.Skipf("MCP server did not start within timeout: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test basic connectivity to the MCP endpoint
|
// First, initialize the session
|
||||||
// The MCP HTTP transport uses a single POST endpoint for all requests
|
initRequest := `{
|
||||||
resp, err := http.Post("http://localhost:8089/mcp", "application/json", bytes.NewBufferString(`{"jsonrpc":"2.0","id":1,"method":"tools/list"}`))
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "initialize",
|
||||||
|
"params": {
|
||||||
|
"protocolVersion": "2025-03-26",
|
||||||
|
"capabilities": {
|
||||||
|
"tools": {},
|
||||||
|
"resources": {},
|
||||||
|
"prompts": {}
|
||||||
|
},
|
||||||
|
"clientInfo": {
|
||||||
|
"name": "test-client",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
initResp, err := http.Post("http://localhost:8091/mcp", "application/json", bytes.NewBufferString(initRequest))
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Initialize request failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := initResp.Body.Close(); err != nil {
|
||||||
|
t.Logf("Error closing init response body: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Read initialization response
|
||||||
|
initBody, err := io.ReadAll(initResp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Failed to read init response body: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Logf("Init response status: %d, body: %s", initResp.StatusCode, string(initBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract session ID from response headers if present
|
||||||
|
sessionID := initResp.Header.Get("Mcp-Session-Id")
|
||||||
|
if sessionID == "" {
|
||||||
|
t.Logf("No session ID in response headers")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now test tools/list with session ID if available
|
||||||
|
headers := map[string]string{
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json,text/event-stream",
|
||||||
|
}
|
||||||
|
if sessionID != "" {
|
||||||
|
headers["Mcp-Session-Id"] = sessionID
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", "http://localhost:8091/mcp", bytes.NewBufferString(`{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Failed to create request: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range headers {
|
||||||
|
req.Header.Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("MCP endpoint test skipped (server might not be fully ready): %v", err)
|
t.Logf("MCP endpoint test skipped (server might not be fully ready): %v", err)
|
||||||
return
|
return
|
||||||
@@ -139,6 +196,14 @@ func TestMCPServerBasicHTTP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Read response body for debugging
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Failed to read response body: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Logf("Response status: %d, body: %s", resp.StatusCode, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
// Accept both 200 and 404 as valid responses (404 means endpoint not implemented)
|
// Accept both 200 and 404 as valid responses (404 means endpoint not implemented)
|
||||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound {
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound {
|
||||||
t.Errorf("MCP endpoint returned unexpected status: %d", resp.StatusCode)
|
t.Errorf("MCP endpoint returned unexpected status: %d", resp.StatusCode)
|
||||||
@@ -168,13 +233,8 @@ func TestMCPServerToolCall(t *testing.T) {
|
|||||||
mcpServer, err := NewMCPServer("8090", aiProvider, true, logger)
|
mcpServer, err := NewMCPServer("8090", aiProvider, true, logger)
|
||||||
assert.NoError(t, err, "Should be able to create MCP server")
|
assert.NoError(t, err, "Should be able to create MCP server")
|
||||||
|
|
||||||
// Start the MCP server in a goroutine
|
// For HTTP mode, the server is already started in NewMCPServer
|
||||||
go func() {
|
// No need to call Start() as it's already running in a goroutine
|
||||||
err := mcpServer.Start()
|
|
||||||
if err != nil {
|
|
||||||
logger.Info("MCP server stopped", zap.Error(err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Wait for the server to start
|
// Wait for the server to start
|
||||||
err = waitForPort("localhost:8090", 10*time.Second)
|
err = waitForPort("localhost:8090", 10*time.Second)
|
||||||
@@ -182,6 +242,39 @@ func TestMCPServerToolCall(t *testing.T) {
|
|||||||
t.Skipf("MCP server did not start within timeout: %v", err)
|
t.Skipf("MCP server did not start within timeout: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First, initialize the session
|
||||||
|
initRequest := `{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "initialize",
|
||||||
|
"params": {
|
||||||
|
"protocolVersion": "2025-03-26",
|
||||||
|
"capabilities": {
|
||||||
|
"tools": {},
|
||||||
|
"resources": {},
|
||||||
|
"prompts": {}
|
||||||
|
},
|
||||||
|
"clientInfo": {
|
||||||
|
"name": "test-client",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
initResp, err := http.Post("http://localhost:8090/mcp", "application/json", bytes.NewBufferString(initRequest))
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Initialize request failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := initResp.Body.Close(); err != nil {
|
||||||
|
t.Logf("Error closing init response body: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Extract session ID from response headers if present
|
||||||
|
sessionID := initResp.Header.Get("Mcp-Session-Id")
|
||||||
|
|
||||||
// Test calling the analyze tool with proper JSON-RPC format
|
// Test calling the analyze tool with proper JSON-RPC format
|
||||||
analyzeRequest := `{
|
analyzeRequest := `{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
@@ -199,7 +292,21 @@ func TestMCPServerToolCall(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
resp, err := http.Post("http://localhost:8090/mcp", "application/json", bytes.NewBufferString(analyzeRequest))
|
// Create request with session ID if available
|
||||||
|
req, err := http.NewRequest("POST", "http://localhost:8090/mcp", bytes.NewBufferString(analyzeRequest))
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Failed to create request: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Accept", "application/json,text/event-stream")
|
||||||
|
if sessionID != "" {
|
||||||
|
req.Header.Set("Mcp-Session-Id", sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("Analyze tool call test skipped (server might not be fully ready): %v", err)
|
t.Logf("Analyze tool call test skipped (server might not be fully ready): %v", err)
|
||||||
return
|
return
|
||||||
|
Reference in New Issue
Block a user