Skip to content
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
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
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,25 @@ bin/
/helm/kagent-tools/Chart.yaml
/reports/tools-cve.csv
.dagger/

# Go build artifacts
*.exe
*.test
vendor/

# Test coverage
coverage.out
coverage.html
*_coverage.out
e2e_coverage.out
integration_coverage.out
telemetry_coverage.out

# Temporary files
*.tmp
*.swp
*~

# IDE
*.iml
Thumbs.db
8 changes: 7 additions & 1 deletion CONTRIBUTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ See the [DEVELOPMENT.md](DEVELOPMENT.md) file for more information.

- **Go Code**:
- Follow the [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)
- Use the official MCP SDK patterns: `github.com/modelcontextprotocol/go-sdk` (Principle I)
- Implement type-safe input validation for all parameters (Principle II)
- Write tests BEFORE implementation - TDD is mandatory (Principle III)
- Maintain modular package design under `pkg/` (Principle IV)
- Use structured logging and sanitize inputs (Principle V)
- Run `make lint` before submitting your changes
- Ensure all tests pass with `make test`
- Add tests for new functionality
- Achieve minimum 80% test coverage
- Follow MCP specification for tool implementations

#### Commit Guidelines

Expand Down
52 changes: 44 additions & 8 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ These tools enhance functionality but aren't required for basic development:
- `istioctl` - Istio service mesh CLI for istio tools
- `cilium` - Cilium CLI for cilium tools

### MCP Tools
```json
{
"mcpServers": {
"kagent-tools": {
"command": "kagent-tools",
"args": ["--stdio", "--kubeconfig", "~/.kube/config", "--tools", "k8s,helm,istio,utils"]
},
"go-sdk-docs": {
"url": "https://gitmcp.io/modelcontextprotocol/go-sdk"
},
"modelcontextprotocol-docs": {
"url": "https://gitmcp.io/modelcontextprotocol/modelcontextprotocol"
}
}
}
```

## Project Structure

```
Expand Down Expand Up @@ -157,7 +175,7 @@ package category

import (
"context"
"github.com/mark3labs/mcp-go/pkg/mcp"
"github.com/modelcontextprotocol/go-sdk/src/go/mcp"
)

type Tools struct {
Expand All @@ -169,11 +187,33 @@ func NewTools() *Tools {
}

func (t *Tools) RegisterTools(server *mcp.Server) {
server.RegisterTool("tool_name", t.handleTool)
tool := mcp.NewTool("tool_name",
mcp.WithDescription("Description of what this tool does"),
mcp.WithString("param1",
mcp.Required(),
mcp.Description("Description of parameter 1"),
),
mcp.WithBool("param2",
mcp.Description("Optional boolean parameter"),
),
)
server.AddTool(tool, t.handleTool)
}

func (t *Tools) handleTool(ctx context.Context, params map[string]interface{}) (*mcp.ToolResult, error) {
// implementation
func (t *Tools) handleTool(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Parse required parameters with type safety
param1, err := request.RequireString("param1")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}

// Parse optional parameters
param2, _ := request.GetBool("param2")

// Tool implementation logic here
result := fmt.Sprintf("Processing %s with flag %v", param1, param2)

return mcp.NewToolResultText(result), nil
}
```

Expand Down Expand Up @@ -250,10 +290,6 @@ func TestToolFunction(t *testing.T) {

```go
func TestIntegration(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}

// Setup test environment
ctx := context.Background()
tools := NewTools()
Expand Down
40 changes: 33 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ FROM $BASE_IMAGE_REGISTRY/chainguard/wolfi-base:latest AS tools
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8

RUN apk update && apk add \
curl openssl bash git ca-certificates \
&& rm -rf /var/cache/apk/*
RUN apk update && apk add --no-cache \
curl openssl bash git ca-certificates go

ARG TARGETARCH
WORKDIR /downloads
Expand All @@ -31,12 +30,36 @@ RUN curl -L https://istio.io/downloadIstio | ISTIO_VERSION=$TOOLS_ISTIO_VERSION
&& rm -rf istio-* \
&& /downloads/istioctl --help

# Install kubectl-argo-rollouts
# Install kubectl-argo-rollouts from source and fix CVE's
# PENDING PR https://github.com/argoproj/argo-rollouts/pull/4515/files
ARG TOOLS_ARGO_ROLLOUTS_VERSION
RUN curl -Lo /downloads/kubectl-argo-rollouts https://github.com/argoproj/argo-rollouts/releases/download/v${TOOLS_ARGO_ROLLOUTS_VERSION}/kubectl-argo-rollouts-linux-${TARGETARCH} \
&& chmod +x /downloads/kubectl-argo-rollouts \
RUN git clone --depth 1 https://github.com/argoproj/argo-rollouts.git -b v${TOOLS_ARGO_ROLLOUTS_VERSION}
RUN cd argo-rollouts \
&& go mod edit -replace=golang.org/x/net=golang.org/x/net@v0.43.0 \
&& go mod edit -replace=golang.org/x/crypto=golang.org/x/crypto@v0.35.0 \
&& go mod edit -replace=k8s.io/kubernetes=k8s.io/kubernetes@v1.34.1 \
&& go mod edit -replace=k8s.io/apimachinery=k8s.io/apimachinery@v0.34.1 \
&& go mod edit -replace=k8s.io/client-go=k8s.io/client-go@v0.34.1 \
&& go mod edit -replace=k8s.io/api=k8s.io/api@v0.34.1 \
&& go mod edit -replace=k8s.io/apiserver=k8s.io/apiserver@v0.34.1 \
&& go mod edit -replace=k8s.io/apiextensions-apiserver=k8s.io/apiextensions-apiserver@v0.34.1 \
&& go mod edit -replace=k8s.io/cli-runtime=k8s.io/cli-runtime@v0.34.1 \
&& go mod edit -replace=k8s.io/kubectl=k8s.io/kubectl@v0.34.1 \
&& go mod edit -replace=k8s.io/code-generator=k8s.io/code-generator@v0.34.1 \
&& go mod edit -replace=github.com/argoproj/notifications-engine=github.com/argoproj/notifications-engine@v0.5.0 \
&& go mod edit -replace=github.com/expr-lang/expr=github.com/expr-lang/expr@v1.17.0 \
&& sed -i 's/v0.30.14/v0.34.1/g' go.mod \
&& sed -i 's/ValidatePodTemplateSpecForReplicaSet(&template, nil, selector,/ValidatePodTemplateSpecForReplicaSet(\&template, selector,/g' pkg/apis/rollouts/validation/validation.go \
&& go mod tidy \
&& CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -ldflags "-s -w" -o /downloads/kubectl-argo-rollouts ./cmd/kubectl-argo-rollouts \
&& /downloads/kubectl-argo-rollouts version

# Install Argo CLI
ARG TOOLS_ARGO_CLI_VERSION
RUN curl -sSL -o /downloads/argocd https://github.com/argoproj/argo-cd/releases/download/v${TOOLS_ARGO_CLI_VERSION}/argocd-linux-${TARGETARCH} \
&& chmod +x /downloads/argocd \
&& /downloads/argocd version --client

# Install Cilium CLI
ARG TOOLS_CILIUM_VERSION
RUN curl -Lo cilium.tar.gz https://github.com/cilium/cilium-cli/releases/download/v${TOOLS_CILIUM_VERSION}/cilium-linux-${TARGETARCH}.tar.gz \
Expand Down Expand Up @@ -88,14 +111,17 @@ FROM gcr.io/distroless/static:nonroot

WORKDIR /
USER 65532:65532
ENV HOME=/home/nonroot
ENV PATH=$PATH:/bin

# Copy the tools
COPY --from=tools --chown=65532:65532 /downloads/kubectl /bin/kubectl
COPY --from=tools --chown=65532:65532 /downloads/istioctl /bin/istioctl
COPY --from=tools --chown=65532:65532 /downloads/helm /bin/helm
COPY --from=tools --chown=65532:65532 /downloads/kubectl-argo-rollouts /bin/kubectl-argo-rollouts
COPY --from=tools --chown=65532:65532 /downloads/cilium /bin/cilium
COPY --from=tools --chown=65532:65532 /downloads/argocd /bin/argocd
COPY --from=tools --chown=65532:65532 /downloads/kubectl-argo-rollouts /bin/kubectl-argo-rollouts

# Copy the tool-server binary
COPY --from=builder --chown=65532:65532 /workspace/tool-server /tool-server

Expand Down
84 changes: 74 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ HELM_DIST_FOLDER ?= $(shell pwd)/dist

.PHONY: clean
clean:
rm -rf ./*.out ./coverage.out ./coverage.html ./*.test
rm -rf ./bin/kagent-tools-*
rm -rf $(HOME)/.local/bin/kagent-tools-*

Expand Down Expand Up @@ -58,7 +59,20 @@ tidy: ## Run go mod tidy to ensure dependencies are up to date.

.PHONY: test
test: build lint ## Run all tests with build, lint, and coverage
go test -tags=test -v -cover ./pkg/... ./internal/...
go test -tags=test -v -cover -coverprofile=coverage.out ./pkg/... || true
@echo ""
@echo "Coverage Report:"
@./scripts/check-coverage.sh coverage.out || true
@echo ""

.PHONY: test-coverage
test-coverage: ## Run tests with coverage output
go test -tags=test -v -cover -coverprofile=coverage.out ./pkg/... ./internal/...

.PHONY: coverage-report
coverage-report: test-coverage ## Generate HTML coverage report
go tool cover -html=coverage.out -o coverage.html
@echo "✅ Coverage report generated: coverage.html"

.PHONY: test-only
test-only: ## Run tests only (without build/lint for faster iteration)
Expand Down Expand Up @@ -114,6 +128,7 @@ run: docker-build
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)
bash ./scripts/kind/setup-kind.sh
@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 @@ -136,17 +151,19 @@ DOCKER_BUILDER ?= docker buildx
DOCKER_BUILD_ARGS ?= --pull --load --platform linux/$(LOCALARCH) --builder $(BUILDX_BUILDER_NAME)

# tools image build args
TOOLS_ISTIO_VERSION ?= 1.27.1
TOOLS_ISTIO_VERSION ?= 1.27.3
TOOLS_ARGO_ROLLOUTS_VERSION ?= 1.8.3
TOOLS_KUBECTL_VERSION ?= 1.34.1
TOOLS_HELM_VERSION ?= 3.19.0
TOOLS_CILIUM_VERSION ?= 0.18.7
TOOLS_CILIUM_VERSION ?= 0.18.8
TOOLS_ARGO_CLI_VERSION ?= 3.1.9

# build args
TOOLS_IMAGE_BUILD_ARGS = --build-arg VERSION=$(VERSION)
TOOLS_IMAGE_BUILD_ARGS += --build-arg LDFLAGS="$(LDFLAGS)"
TOOLS_IMAGE_BUILD_ARGS += --build-arg LOCALARCH=$(LOCALARCH)
TOOLS_IMAGE_BUILD_ARGS += --build-arg TOOLS_ISTIO_VERSION=$(TOOLS_ISTIO_VERSION)
TOOLS_IMAGE_BUILD_ARGS += --build-arg TOOLS_ARGO_CLI_VERSION=$(TOOLS_ARGO_CLI_VERSION)
TOOLS_IMAGE_BUILD_ARGS += --build-arg TOOLS_ARGO_ROLLOUTS_VERSION=$(TOOLS_ARGO_ROLLOUTS_VERSION)
TOOLS_IMAGE_BUILD_ARGS += --build-arg TOOLS_KUBECTL_VERSION=$(TOOLS_KUBECTL_VERSION)
TOOLS_IMAGE_BUILD_ARGS += --build-arg TOOLS_HELM_VERSION=$(TOOLS_HELM_VERSION)
Expand Down Expand Up @@ -178,9 +195,10 @@ helm-uninstall:
helm uninstall kagent --namespace kagent --kube-context kind-$(KIND_CLUSTER_NAME) --wait

.PHONY: helm-install
helm-install: helm-version
helm-install: helm-version retag
#delete first to allow testing with kagent
helm template kagent-tools ./helm/kagent-tools --namespace kagent | kubectl --namespace kagent delete -f - || :
helm $(HELM_ACTION) kagent-tools ./helm/kagent-tools \
--kube-context kind-$(KIND_CLUSTER_NAME) \
--namespace kagent \
--create-namespace \
--history-max 2 \
Expand Down Expand Up @@ -212,15 +230,37 @@ otel-local:
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: tools-install
tools-install: clean
.PHONY: install/argocd
install/argocd:
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

.PHONY: install/istio
install/istio:
istioctl install --set profile=demo -y

.PHONY: install/kagent
install/kagent:
@echo "Installing kagent in namespace 'kagent' ..."
which kagent || curl https://raw.githubusercontent.com/kagent-dev/kagent/refs/heads/main/scripts/get-kagent | bash
kagent install -n kagent

.PHONY: install/tools
install/tools: clean
mkdir -p $(HOME)/.local/bin
go build -ldflags "$(LDFLAGS)" -o $(LOCALBIN)/kagent-tools ./cmd
go build -ldflags "$(LDFLAGS)" -o $(HOME)/.local/bin/kagent-tools ./cmd
$(HOME)/.local/bin/kagent-tools --version

.PHONY: docker-build install
install: install/tools install/kagent install/istio install/argocd helm-install

.PHONY: dashboard/kagent
dashboard/kagent:
kagent dashboard -n kagent

.PHONY: run-agentgateway
run-agentgateway: tools-install
run-agentgateway: install/tools
open http://localhost:15000/ui
cd scripts \
&& agentgateway -f agentgateway-config-tools.yaml
Expand All @@ -233,17 +273,41 @@ report/image-cve: docker-build govulncheck
## Tool Binaries
## Location to install dependencies t

# check-release-version checks if a tool version matches the latest GitHub release
# $1 - variable name (e.g., TOOLS_ISTIO_VERSION)
# $2 - current version value
# $3 - GitHub repo (e.g., istio/istio)
define check-release-version
@LATEST=$$(gh release list --repo $(3) --json tagName,isLatest | jq -r '.[] | select(.isLatest==true) | .tagName'); \
if [ "$(2)" = "$${LATEST#v}" ]; then \
echo "✅ $(1)=$(2) == $$LATEST"; \
else \
echo "❌ $(1)=$(2) != $$LATEST"; \
fi
endef

.PHONY: check-releases
check-releases:
@echo "Checking tool versions against latest releases..."
@echo ""
$(call check-release-version,TOOLS_ARGO_ROLLOUTS_VERSION,$(TOOLS_ARGO_ROLLOUTS_VERSION),argoproj/argo-rollouts)
$(call check-release-version,TOOLS_ARGO_CLI_VERSION,$(TOOLS_ARGO_CLI_VERSION),argoproj/argo-cd)
$(call check-release-version,TOOLS_CILIUM_VERSION,$(TOOLS_CILIUM_VERSION),cilium/cilium-cli)
$(call check-release-version,TOOLS_ISTIO_VERSION,$(TOOLS_ISTIO_VERSION),istio/istio)
$(call check-release-version,TOOLS_HELM_VERSION,$(TOOLS_HELM_VERSION),helm/helm)
$(call check-release-version,TOOLS_KUBECTL_VERSION,$(TOOLS_KUBECTL_VERSION),kubernetes/kubernetes)

.PHONY: $(LOCALBIN)
$(LOCALBIN):
mkdir -p $(LOCALBIN)

GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
GOLANGCI_LINT_VERSION ?= v1.63.4
GOLANGCI_LINT_VERSION ?= v2.5.0

.PHONY: golangci-lint
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
$(GOLANGCI_LINT): $(LOCALBIN)
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))

# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
# $1 - target path with name of binary
Expand Down
Loading
Loading