diff --git a/Makefile b/Makefile index 6be3920..8f9fcf9 100644 --- a/Makefile +++ b/Makefile @@ -19,11 +19,13 @@ LDFLAGS := -X github.com/kagent-dev/tools/internal/version.Version=$(VERSION) -X ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin +PATH := $HOME/local/bin:/opt/homebrew/bin/:$(LOCALBIN):$(PATH) HELM_DIST_FOLDER ?= $(shell pwd)/dist .PHONY: clean clean: rm -rf ./bin/kagent-tools-* + rm -rf $(HOME)/.local/bin/kagent-tools-* .PHONY: fmt fmt: ## Run go fmt against code. @@ -210,6 +212,19 @@ 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 + 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: run-agentgateway +run-agentgateway: tools-install + open http://localhost:15000/ui + cd scripts \ + && agentgateway -f agentgateway-config-tools.yaml + .PHONY: report/image-cve report/image-cve: docker-build govulncheck echo "Running CVE scan :: CVE -> CSV ... reports/$(SEMVER)/" diff --git a/README.md b/README.md index f26ccf9..0bc29be 100644 --- a/README.md +++ b/README.md @@ -28,16 +28,26 @@ This directory contains the Go implementation of all KAgent tools, migrated from - **Bash:** -`curl -sL https://github.com/kagent-dev/tools/blob/main/scripts/install.sh | bash` +```bash +curl -sL https://raw.githubusercontent.com/kagent-dev/tools/refs/heads/main/scripts/install.sh | bash +``` - **Docker:** -`docker run -it --rm ghcr.io/kagent-dev/kagent/tools:` +```bash +docker run -it --rm -p 8084:8084 ghcr.io/kagent-dev/kagent/tools:0.0.10 +``` - **Kubernetes** -`helm upgrade -i kagent-tools --version oci://ghcr.io/kagent-dev/tools/helm/` +```bash +helm upgrade -i -n kagent --create-namespace kagent-tools oci://ghcr.io/kagent-dev/tools/helm/kagent-tools --version 0.0.10 +helm ls -A +``` + +## Quickstart Guide +For a quickstart guide on how to run KAgent tools using AgentGateway, please refer to the [Quickstart Guide](docs/quickstart.md). ## Architecture diff --git a/cmd/main.go b/cmd/main.go index 03a2665..fa737dd 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -89,7 +89,7 @@ func run(cmd *cobra.Command, args []string) { return } - logger.Init() + logger.Init(stdio) defer logger.Sync() // Setup context with cancellation for graceful shutdown diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 0000000..85b86cc --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,78 @@ + +# Quickstart Guide for KAgnet Tools + +## About this guide + +This guide provides a quick overview of how to set up and run KAgent tools using AgentGateway. + +For more detaled information on KAgent tools, please refer to the [KAgent Tools Documentation](https://kagent.dev/tools). + +To learn more about agentgateway, see [AgentGateway](https://agentgateway.dev/docs/about/) + +### Running KAgent Tools using AgentGateway + +1. Download tools binary and install it. +2. Download tools configuration file for agentgateway. +3. Download the agentgateway binary and install it. +4. Run the agentgateway with the configuration file. +5. open http://localhost:15000/ui + +```bash +curl -sL https://raw.githubusercontent.com/kagent-dev/tools/refs/heads/main/scripts/install.sh | bash +curl -sL https://raw.githubusercontent.com/kagent-dev/tools/refs/heads/main/scripts/agentgateway-config-tools.yaml +curl -sL https://raw.githubusercontent.com/agentgateway/agentgateway/refs/heads/main/common/scripts/get-agentproxy | bash + +export PATH=$PATH:$HOME/.local/bin/ +agentgateway -f agentgateway-config-tools.yaml +``` + +agentgateway-config-tools.yaml: +```yaml +binds: + - port: 30805 + listeners: + - routes: + - policies: + cors: + allowOrigins: + - "*" + allowHeaders: + - mcp-protocol-version + - content-type + backends: + - mcp: + name: default + targets: + - name: kagent-tools + stdio: + cmd: kagent-tools + args: ["--stdio", "--kubeconfig", "~/.kube/config"] +``` +Afterwards, you can run it with make command +```bash +make run-agentgateway +``` + +### Running KAgent Tools using Cursor MCP + + +1. Download the agentgateway binary and install it. +``` +curl -sL https://raw.githubusercontent.com/kagent-dev/tools/refs/heads/main/scripts/install.sh | bash +``` + +2. Create `.cursor/mcp.json` + +```json +{ + "mcpServers": { + "kagent-tools": { + "command": "kagent-tools", + "args": ["--stdio", "--kubeconfig", "~/.kube/config"] + } + } +} +``` + + + diff --git a/helm/kagent-tools/Chart-template.yaml b/helm/kagent-tools/Chart-template.yaml index 6fb1a68..3d2eb2b 100644 --- a/helm/kagent-tools/Chart-template.yaml +++ b/helm/kagent-tools/Chart-template.yaml @@ -1,5 +1,6 @@ apiVersion: v2 name: kagent-tools -description: A Helm chart for kagent-tools, +description: A Helm chart for kagent-tools type: application version: ${VERSION} +appVersion: ${VERSION} \ No newline at end of file diff --git a/internal/commands/builder.go b/internal/commands/builder.go index f3e75ce..e0ec477 100644 --- a/internal/commands/builder.go +++ b/internal/commands/builder.go @@ -15,6 +15,13 @@ import ( "go.opentelemetry.io/otel/attribute" ) +const ( + // DefaultTimeout is the default timeout for command execution + DefaultTimeout = 2 * time.Minute + // DefaultCacheTTL is the default cache TTL + DefaultCacheTTL = 1 * time.Minute +) + // CommandBuilder provides a fluent interface for building CLI commands type CommandBuilder struct { command string @@ -43,10 +50,10 @@ func NewCommandBuilder(command string) *CommandBuilder { args: make([]string, 0), labels: make(map[string]string), annotations: make(map[string]string), - timeout: 60 * time.Second, + timeout: DefaultTimeout, useTimeout: false, // Only enable timeout when explicitly requested validate: true, - cacheTTL: 1 * time.Minute, + cacheTTL: DefaultCacheTTL, } } diff --git a/internal/commands/builder_test.go b/internal/commands/builder_test.go index 52dc0ec..377afeb 100644 --- a/internal/commands/builder_test.go +++ b/internal/commands/builder_test.go @@ -19,8 +19,8 @@ func TestNewCommandBuilder(t *testing.T) { assert.Empty(t, cb.output) assert.NotNil(t, cb.labels) assert.NotNil(t, cb.annotations) - assert.Equal(t, 60*time.Second, cb.timeout) - assert.Equal(t, 1*time.Minute, cb.cacheTTL) + assert.Equal(t, DefaultTimeout, cb.timeout) + assert.Equal(t, DefaultCacheTTL, cb.cacheTTL) assert.True(t, cb.validate) assert.False(t, cb.cached) assert.False(t, cb.dryRun) diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 041d499..b9a078f 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -10,23 +10,39 @@ import ( var globalLogger *slog.Logger -func Init() { +// Init initializes the global logger +// If useStderr is true, logs will be written to stderr (for stdio mode) +// If useStderr is false, logs will be written to stdout (for HTTP mode) +func Init(useStderr bool) { opts := &slog.HandlerOptions{ Level: slog.LevelInfo, } + // Choose output destination based on mode + output := os.Stdout + if useStderr { + output = os.Stderr + } + if os.Getenv("KAGENT_LOG_FORMAT") == "json" { - globalLogger = slog.New(slog.NewJSONHandler(os.Stdout, opts)) + globalLogger = slog.New(slog.NewJSONHandler(output, opts)) } else { - globalLogger = slog.New(slog.NewTextHandler(os.Stdout, opts)) + globalLogger = slog.New(slog.NewTextHandler(output, opts)) } slog.SetDefault(globalLogger) } +// InitWithEnv initializes the logger using environment variables +// This is a convenience function that defaults to stdout unless KAGENT_USE_STDERR is set +func InitWithEnv() { + useStderr := os.Getenv("KAGENT_USE_STDERR") == "true" + Init(useStderr) +} + func Get() *slog.Logger { if globalLogger == nil { - Init() + InitWithEnv() } return globalLogger } diff --git a/internal/logger/logger_test.go b/internal/logger/logger_test.go index ad5c988..efca71e 100644 --- a/internal/logger/logger_test.go +++ b/internal/logger/logger_test.go @@ -64,7 +64,8 @@ func TestGet(t *testing.T) { } func TestInit(t *testing.T) { - assert.NotPanics(t, Init) + assert.NotPanics(t, func() { Init(false) }) + assert.NotPanics(t, func() { Init(true) }) } func TestSync(t *testing.T) { diff --git a/scripts/agentgateway-config-tools.yaml b/scripts/agentgateway-config-tools.yaml new file mode 100644 index 0000000..67863bb --- /dev/null +++ b/scripts/agentgateway-config-tools.yaml @@ -0,0 +1,23 @@ +binds: +- port: 30805 + listeners: + - routes: + - backends: + - mcp: + name: default + targets: + - name: kagent-tools + stdio: + cmd: kagent-tools + args: + - --stdio + - --kubeconfig + - ~/.kube/config + policies: + cors: + allowOrigins: + - '*' + allowHeaders: + - mcp-protocol-version + - content-type + - cache-control