Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
fbf60ff
git ignore
dimetron Jul 8, 2025
6481100
Merge remote-tracking branch 'origin/main'
dimetron Jul 8, 2025
ea6f80d
draft ROADMAP.md
dimetron Jul 8, 2025
10e82e4
telemetry
dimetron Jul 9, 2025
402027e
telemetry
dimetron Jul 9, 2025
d0c75d0
- added telemetry
dimetron Jul 10, 2025
f5d0dd9
- added telemetry
dimetron Jul 10, 2025
543a544
- improved test coverage
dimetron Jul 10, 2025
265290d
Merge branch 'main' into feature/tools-refactoring
dimetron Jul 10, 2025
371fe4f
- fix OTEL protocols
dimetron Jul 10, 2025
a20ed57
- add OTEL insecure
dimetron Jul 10, 2025
1d9b55d
- code cleanup
dimetron Jul 10, 2025
eec0960
- fix invalid parameter
dimetron Jul 10, 2025
e6e80b1
- fix invalid parameter
dimetron Jul 10, 2025
bc7fc60
fix http context propagation
dimetron Jul 10, 2025
d43a178
remove ROADMAP
dimetron Jul 10, 2025
5242090
GO 1.24.5
dimetron Jul 10, 2025
aeafcde
e2e increase timeout
dimetron Jul 10, 2025
01c2f82
e2e increase timeout
dimetron Jul 10, 2025
46e332d
e2e fix checks
dimetron Jul 10, 2025
a827c90
cleanup - resolve PR reviews
dimetron Jul 12, 2025
86edecf
add span on builder
dimetron Jul 12, 2025
fd86663
- separate tools helm chart
dimetron Jul 19, 2025
5c019d1
kind cluster for GHA
dimetron Jul 19, 2025
8bb9b2b
e2e GetMCPClient
dimetron Jul 19, 2025
7302704
e2e fixed path
dimetron Jul 19, 2025
c69e992
use mcp client in test
dimetron Jul 19, 2025
032cb2d
basic e2e with k8s
dimetron Jul 19, 2025
6526487
e2e with k8s
dimetron Jul 19, 2025
20e2e40
resolve conflicts in e2e tests
dimetron Jul 19, 2025
794e5ff
error handling e2e
dimetron Jul 19, 2025
e32bf24
cluster admin
dimetron Jul 19, 2025
f2a8f0c
fix timeout issue
dimetron Jul 19, 2025
6ac54b3
fix argo test
dimetron Jul 20, 2025
c921da3
fix error handling
dimetron Jul 20, 2025
b6c15ed
fix linting error
dimetron Jul 20, 2025
66f2534
install script
dimetron Jul 20, 2025
f9d6f01
change helm publish name
dimetron Jul 21, 2025
98217f9
move e2e -> test
dimetron Jul 22, 2025
b21832f
e2e -> test
dimetron Jul 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ jobs:
go-version: "1.24"
cache: true

- name: Create k8s Kind Cluster
uses: helm/kind-action@v1
with:
cluster_name: kagent
config: scripts/kind/kind-config.yaml

- name: Run cmd/main.go tests
working-directory: .
run: |
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/tag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
export VERSION=$(echo "$GITHUB_REF" | cut -c12-)
fi
make docker-build
make helm-publish
release:
# Only run release after images and helm chart are pushed
# In the future we can take the chart from the helm action,
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ bin/
/kagent-tools
/*.out
*.html
/helm/kagent-tools/Chart.yaml
/reports/tools-cve.csv
74 changes: 64 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
DOCKER_REGISTRY ?= ghcr.io
BASE_IMAGE_REGISTRY ?= cgr.dev

DOCKER_REPO ?= kagent-dev/kagent

HELM_REPO ?= oci://ghcr.io/kagent-dev
HELM_ACTION=upgrade --install

KIND_CLUSTER_NAME ?= kagent
KIND_IMAGE_VERSION ?= 1.33.1
KIND_CREATE_CMD ?= "kind create cluster --name $(KIND_CLUSTER_NAME) --image kindest/node:v$(KIND_IMAGE_VERSION) --config ./scripts/kind/kind-config.yaml"

BUILD_DATE := $(shell date -u '+%Y-%m-%d')
GIT_COMMIT := $(shell git rev-parse --short HEAD || echo "unknown")
Expand All @@ -12,6 +19,7 @@ LDFLAGS := -X github.com/kagent-dev/tools/internal/version.Version=$(VERSION) -X

## Location to install dependencies to
LOCALBIN ?= $(shell pwd)/bin
HELM_DIST_FOLDER ?= $(shell pwd)/dist

.PHONY: clean
clean:
Expand Down Expand Up @@ -55,8 +63,8 @@ test-only: ## Run tests only (without build/lint for faster iteration)
go test -tags=test -v -cover ./pkg/... ./internal/...

.PHONY: e2e
e2e: test docker-build
go test -tags=test -v -cover ./e2e/...
e2e: test retag
go test -v -tags=test -cover ./test/e2e/ -timeout 5m

bin/kagent-tools-linux-amd64:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/kagent-tools-linux-amd64 ./cmd
Expand Down Expand Up @@ -89,7 +97,7 @@ bin/kagent-tools-windows-amd64.exe.sha256: bin/kagent-tools-windows-amd64.exe
sha256sum bin/kagent-tools-windows-amd64.exe > bin/kagent-tools-windows-amd64.exe.sha256

.PHONY: build
build: $(LOCALBIN) bin/kagent-tools-linux-amd64.sha256 bin/kagent-tools-linux-arm64.sha256 bin/kagent-tools-darwin-amd64.sha256 bin/kagent-tools-darwin-arm64.sha256 bin/kagent-tools-windows-amd64.exe.sha256
build: $(LOCALBIN) clean bin/kagent-tools-linux-amd64.sha256 bin/kagent-tools-linux-arm64.sha256 bin/kagent-tools-darwin-amd64.sha256 bin/kagent-tools-darwin-arm64.sha256 bin/kagent-tools-windows-amd64.exe.sha256
build:
@echo "Build complete. Binaries are available in the bin/ directory."
ls -lt bin/kagent-tools-*
Expand All @@ -100,8 +108,10 @@ run: docker-build
@echo "Use: npx @modelcontextprotocol/inspector to connect to the tool server"
@docker run --rm --net=host -p 8084:8084 -e OPENAI_API_KEY=$(OPENAI_API_KEY) -v $(HOME)/.kube:/home/nonroot/.kube -e KAGENT_TOOLS_PORT=8084 $(TOOLS_IMG) -- --kubeconfig /root/.kube/config

PHONY: retag
retag: docker-build
.PHONY: retag
retag: docker-build helm-version
@echo "Check Kind cluster $(KIND_CLUSTER_NAME) exists"
kind get clusters | grep -q $(KIND_CLUSTER_NAME) || bash -c $(KIND_CREATE_CMD)
@echo "Retagging tools image to $(RETAGGED_TOOLS_IMG)"
docker tag $(TOOLS_IMG) $(RETAGGED_TOOLS_IMG)
kind load docker-image --name $(KIND_CLUSTER_NAME) $(RETAGGED_TOOLS_IMG)
Expand All @@ -127,7 +137,7 @@ DOCKER_BUILD_ARGS ?= --pull --load --platform linux/$(LOCALARCH) --builder $(BUI
TOOLS_ISTIO_VERSION ?= 1.26.2
TOOLS_ARGO_ROLLOUTS_VERSION ?= 1.8.3
TOOLS_KUBECTL_VERSION ?= 1.33.2
TOOLS_HELM_VERSION ?= 3.18.3
TOOLS_HELM_VERSION ?= 3.18.4
TOOLS_CILIUM_VERSION ?= 0.18.5

# build args
Expand Down Expand Up @@ -155,11 +165,55 @@ docker-build-all: DOCKER_BUILD_ARGS = --progress=plain --builder $(BUILDX_BUILDE
docker-build-all:
$(DOCKER_BUILDER) build $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -f Dockerfile ./

.PHONY: helm-version
helm-version:
VERSION=$(VERSION) envsubst < helm/kagent-tools/Chart-template.yaml > helm/kagent-tools/Chart.yaml
mkdir -p $(HELM_DIST_FOLDER)
helm package -d $(HELM_DIST_FOLDER) helm/kagent-tools

.PHONY: helm-uninstall
helm-uninstall:
helm uninstall kagent --namespace kagent --kube-context kind-$(KIND_CLUSTER_NAME) --wait

.PHONY: helm-install
helm-install: helm-version
helm $(HELM_ACTION) kagent-tools ./helm/kagent-tools \
--kube-context kind-$(KIND_CLUSTER_NAME) \
--namespace kagent \
--create-namespace \
--history-max 2 \
--timeout 5m \
-f ./scripts/kind/test-values.yaml \
--set tools.image.registry=$(RETAGGED_DOCKER_REGISTRY) \
--wait

.PHONY: helm-publish
helm-publish: helm-version
helm push ./$(HELM_DIST_FOLDER)/kagent-tools-$(VERSION).tgz $(HELM_REPO)/tools/helm

.PHONY: create-kind-cluster
create-kind-cluster:
docker pull kindest/node:v$(KIND_IMAGE_VERSION) || true
bash -c $(KIND_CREATE_CMD)

.PHONY: delete-kind-cluster
delete-kind-cluster:
kind delete cluster --name $(KIND_CLUSTER_NAME)

.PHONY: kind-update-kagent
kind-update-kagent: docker-build
kind get clusters | grep -q $(KIND_CLUSTER_NAME) || kind create cluster --name $(KIND_CLUSTER_NAME)
kind load docker-image --name $(KIND_CLUSTER_NAME) $(TOOLS_IMG)
kubectl patch --namespace kagent deployment/kagent --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/3/image", "value": "$(TOOLS_IMG)"}]'
kind-update-kagent: retag
kubectl patch --namespace kagent deployment/kagent --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/3/image", "value": "$(RETAGGED_TOOLS_IMG)"}]'

.PHONY: otel-local
otel-local:
docker rm -f jaeger-desktop || true
docker run -d --name jaeger-desktop --restart=always -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/jaeger:2.7.0
open http://localhost:16686/

.PHONY: report/image-cve
report/image-cve: docker-build govulncheck
echo "Running CVE scan :: CVE -> CSV ... reports/$(SEMVER)/"
grype docker:$(TOOLS_IMG) -o template -t reports/cve-report.tmpl --file reports/$(SEMVER)/tools-cve.csv

## Tool Binaries
## Location to install dependencies t
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@

This directory contains the Go implementation of all KAgent tools, migrated from the original Python implementation. The tools are designed to work with the Model Context Protocol (MCP) server and provide comprehensive Kubernetes, cloud-native, and observability functionality.

## Installation

- **Bash:**

`curl -sL https://github.com/kagent-dev/tools/blob/main/scripts/install.sh | bash`

- **Docker:**

`docker run -it --rm ghcr.io/kagent-dev/kagent/tools:<version>`

- **Kubernetes**

`helm upgrade -i kagent-tools --version <version> oci://ghcr.io/kagent-dev/tools/helm/`


## Architecture

The Go tools are implemented as a single MCP server that exposes all available tools through the MCP protocol.
Expand Down
30 changes: 25 additions & 5 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ import (
)

var (
port int
stdio bool
tools []string
kubeconfig *string
port int
stdio bool
tools []string
kubeconfig *string
showVersion bool

// These variables should be set during build time using -ldflags
Name = "kagent-tools-server"
Expand All @@ -55,6 +56,7 @@ func init() {
rootCmd.Flags().IntVarP(&port, "port", "p", 8084, "Port to run the server on")
rootCmd.Flags().BoolVar(&stdio, "stdio", false, "Use stdio for communication instead of HTTP")
rootCmd.Flags().StringSliceVar(&tools, "tools", []string{}, "List of tools to register. If empty, all tools are registered.")
rootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "Show version information and exit")
kubeconfig = rootCmd.Flags().String("kubeconfig", "", "kubeconfig file path (optional, defaults to in-cluster config)")

// if found .env file, load it
Expand All @@ -70,7 +72,23 @@ func main() {
}
}

// printVersion displays version information in a formatted way
func printVersion() {
fmt.Printf("%s\n", Name)
fmt.Printf("Version: %s\n", Version)
fmt.Printf("Git Commit: %s\n", GitCommit)
fmt.Printf("Build Date: %s\n", BuildDate)
fmt.Printf("Go Version: %s\n", runtime.Version())
fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
}

func run(cmd *cobra.Command, args []string) {
// Handle version flag early, before any initialization
if showVersion {
printVersion()
return
}

logger.Init()
defer logger.Sync()

Expand Down Expand Up @@ -130,7 +148,9 @@ func run(cmd *cobra.Command, args []string) {
runStdioServer(ctx, mcp)
}()
} else {
sseServer := server.NewStreamableHTTPServer(mcp)
sseServer := server.NewStreamableHTTPServer(mcp,
server.WithHeartbeatInterval(30*time.Second),
)

// Create a mux to handle different routes
mux := http.NewServeMux()
Expand Down
Loading