Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"build": {
"dockerfile": "Dockerfile",
"args": {
"TOOLS_GO_VERSION": "1.25",
"TOOLS_GO_VERSION": "1.27",
"TOOLS_ISTIO_VERSION": "1.28.3",
"TOOLS_ARGO_ROLLOUTS_VERSION": "1.8.3",
"TOOLS_KUBECTL_VERSION": "1.35.0",
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,35 @@ jobs:
run: |
make test

plugin-verify:
env:
VERSION: v0.0.1-test
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
cache: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: v0.23.0
use: 'true'
- name: Verify kagent Agent Plugin image
env:
DOCKER_BUILDKIT: 1
BUILDX_BUILDER_NAME: kagent-builder-v0.23.0
DOCKER_BUILD_ARGS: >-
--cache-from=type=gha
--load
--platform=linux/amd64
DOCKER_REPO: "${{ github.repository_owner }}/tools"
DOCKER_BUILDER: "docker buildx"
run: make plugin-verify

go-e2e-tests:
runs-on: ubuntu-latest
steps:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ bin/
/helm/kagent-tools/Chart.yaml
/reports/tools-cve.csv
.dagger/

# Plugin MCP config is a release artifact; keep it tracked even when a
# global gitignore excludes mcp.json files.
!plugin/mcp.json
15 changes: 14 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ tools/
│ └── version/ # Version info
├── test/
│ └── e2e/ # End-to-end tests (Kind cluster)
├── plugin/ # kagent Agent Plugin sources (plugin.json, mcp.json, kubeconfig, skills/)
├── scripts/ # Build and setup scripts
│ └── kind/ # Kind cluster configuration
├── helm/ # Helm chart for deployment
Expand All @@ -73,7 +74,7 @@ tools/
│ └── tag.yaml # Release tagging
├── Makefile # Build orchestration
├── Dockerfile # Multi-stage build (multi-arch)
├── go.mod # Go 1.27.0
├── go.mod # Go 1.27.3
├── DEVELOPMENT.md # Development setup and standards
└── CONTRIBUTION.md # Contribution process
```
Expand Down Expand Up @@ -359,6 +360,18 @@ Types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf`, `ci`
10. Add E2E tests if the tool interacts with a cluster.
11. Run `make fmt && make lint && make test` before submitting.

### Plugin bundle (kagent 1.x skills)

The image doubles as a kagent Agent Plugin (see [docs/kagent-skills.md](docs/kagent-skills.md)).
New tool providers should add or extend a skill in `plugin/skills/<name>/SKILL.md` and keep
`cmd/plugin_bundle_test.go` passing; it checks manifests, `mcp.json`, skill frontmatter, that
every tool a skill names is registered, and that every `` `argument: value` `` on a skill line is an
input of a tool named on that line. `cmd/plugin_docs_test.go` checks the `AgentTemplate` examples
in `docs/kagent-skills.md` against kagent's rules (`modelConfig`, at most 50 bound tools, and a
`tools:` list that covers the selected skills). Skills must only name tools that work with the
chart defaults (no tool that needs an unconfigured LLM key). Run `make plugin-verify` after
changing the `Dockerfile` or `plugin/`.

---

## Additional Resources
Expand Down
2 changes: 1 addition & 1 deletion DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This document provides comprehensive development guidelines for the KAgent Tools

### Required Tools

- **Go 1.24+** - Primary development language
- **Go 1.27+** - Primary development language
- **Docker** - For containerization and testing
- **Make** - Build automation
- **Git** - Version control
Expand Down
58 changes: 43 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -80,30 +80,58 @@ COPY pkg pkg
RUN --mount=type=cache,target=/root/go/pkg/mod,rw \
--mount=type=cache,target=/root/.cache/go-build,rw \
echo "Building tool-server for $TARGETARCH on $BUILDARCH" && \
CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -ldflags "$LDFLAGS" -o tool-server cmd/main.go
CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -trimpath -ldflags "-s -w $LDFLAGS" -o tool-server ./cmd

### STAGE 3: assemble kagent Agent Plugin roots
# Only the small files and links are assembled here, on their own base so CLI
# downloads do not invalidate this stage. Binaries are copied in the final stage.
ARG BASE_IMAGE_REGISTRY=cgr.dev
FROM $BASE_IMAGE_REGISTRY/chainguard/wolfi-base:latest AS plugin
ARG VERSION=dev
COPY plugin /src/plugin
RUN set -eu; \
root=/out/opt/kagent-tools; skills_root=/out/opt/kagent-tools-skills; \
plugin_version="${VERSION#v}"; \
mkdir -p "$root/bin" "$root/config" "$skills_root" /out/usr/bin; \
sed "s/__VERSION__/${plugin_version}/" /src/plugin/plugin.json.tmpl > "$root/plugin.json"; \
sed "s/__VERSION__/${plugin_version}/" /src/plugin/skills-plugin.json.tmpl > "$skills_root/plugin.json"; \
install -m 0644 /src/plugin/mcp.json "$root/mcp.json"; \
install -m 0644 /src/plugin/config/kubeconfig "$root/config/kubeconfig"; \
cp -R /src/plugin/skills "$root/skills"; \
cp -R /src/plugin/skills "$skills_root/skills"; \
find /out/opt -type d -exec chmod 0755 {} +; \
find /out/opt -type f ! -path '*/bin/*' -exec chmod 0644 {} +; \
ln -s /opt/kagent-tools/bin/kagent-tools /out/tool-server; \
for binary in kubectl helm istioctl kubectl-argo-rollouts cilium; do \
ln -s "/opt/kagent-tools/bin/$binary" "/out/usr/bin/$binary"; \
done; \
chown -R 65532:65532 /out/opt

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot

WORKDIR /
USER 65532:65532
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 the tool-server binary
COPY --from=builder --chown=65532:65532 /workspace/tool-server /tool-server
ENV PATH=/opt/kagent-tools/bin:$PATH:/bin

# /opt/kagent-tools -> kagent Agent Plugin root (CLIs + stdio MCP server + skills)
# /opt/kagent-tools-skills -> skills-only Agent Plugin root
# /tool-server, /bin/* -> backward-compatible symlinks (distroless /bin -> usr/bin)
# Three content layers, ordered from least to most frequently changed:
# 1. all CLIs (~500 MiB, change only on a version bump)
# 2. plugin manifests, skills and links (a few KiB)
# 3. the Go server (changes on every commit, ~70 MiB)
# so a Go or skill change re-ships only the small layers.
COPY --from=tools --chown=65532:65532 --chmod=0755 \
/downloads/kubectl /downloads/helm /downloads/istioctl /downloads/kubectl-argo-rollouts /downloads/cilium \
/opt/kagent-tools/bin/
COPY --from=plugin /out/ /
Comment thread
dimetron marked this conversation as resolved.
COPY --from=builder --chown=65532:65532 --chmod=0755 /workspace/tool-server /opt/kagent-tools/bin/kagent-tools

ARG VERSION

LABEL org.opencontainers.image.source=https://github.com/kagent-dev/tools
LABEL org.opencontainers.image.description="Kagent MCP tools server"
LABEL org.opencontainers.image.description="Kagent MCP tools server and kagent Agent Plugin (skills bundle)"
LABEL org.opencontainers.image.authors="Kagent Creators 🤖"
LABEL org.opencontainers.image.version="$VERSION"

ENTRYPOINT ["/tool-server"]
ENTRYPOINT ["/opt/kagent-tools/bin/kagent-tools"]
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ buildx-create:
docker-build: fmt buildx-create
$(DOCKER_BUILDER) build $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(TOOLS_IMG) -f Dockerfile ./

.PHONY: plugin-verify
plugin-verify: docker-build ## Verify the image is a valid kagent Agent Plugin (layout, limits, stdio)
./scripts/verify-plugin-image.sh $(TOOLS_IMG)

.PHONY: docker-build # build tools image for amd64 and arm64
docker-build-all: fmt buildx-create
docker-build-all: DOCKER_BUILD_ARGS = --progress=plain --builder $(BUILDX_BUILDER_NAME) --platform linux/amd64,linux/arm64 --output type=tar,dest=/dev/null
Expand Down Expand Up @@ -278,7 +282,7 @@ $(LOCALBIN):
mkdir -p $(LOCALBIN)

GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
GOLANGCI_LINT_VERSION ?= v2.13.2
GOLANGCI_LINT_VERSION ?= v2.15.0

.PHONY: golangci-lint
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ helm upgrade -i -n kagent --create-namespace kagent-tools oci://ghcr.io/kagent-d
helm ls -A
```

- **kagent 1.x skills bundle (OCI image)**

The same image is a kagent Agent Plugin: mount it in an `AgentTemplate` with `source.oci`
(digest-pinned) and `path: opt/kagent-tools-skills` to get the operational skills, and bind the
tools through a `RemoteMCPServer`. The full root, `opt/kagent-tools` (MCP tools, all CLIs and
skills in one bundle), is over kagent's 100 MiB package limit and needs a future kagent release.
See [Using kagent-tools as a kagent 1.x skills bundle](docs/kagent-skills.md).

## Quickstart Guide

For a quickstart guide on how to run KAgent tools using AgentGateway, please refer to the [Quickstart Guide](docs/quickstart.md).
Expand Down
21 changes: 19 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,23 @@ func run(cmd *cobra.Command, args []string) {
metricsPort = port
}

effectiveConfig, err := resolveServerConfig(serverFlags{
Tools: tools,
ToolsSet: cmd.Flags().Changed("tools"),
ReadOnly: readOnly,
ReadOnlySet: cmd.Flags().Changed("read-only"),
Kubeconfig: *kubeconfig,
}, os.LookupEnv)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
tools, readOnly = effectiveConfig.Tools, effectiveConfig.ReadOnly
if err := applyServerEnvironment(effectiveConfig, stdio, os.Setenv); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

logger.Init(stdio)
defer logger.Sync()

Expand All @@ -121,7 +138,7 @@ func run(cmd *cobra.Command, args []string) {
// Initialize OpenTelemetry tracing
cfg := telemetry.LoadOtelCfg()

err := telemetry.SetupOTelSDK(ctx)
err = telemetry.SetupOTelSDK(ctx)
if err != nil {
logger.Get().Error("Failed to setup OpenTelemetry SDK", "error", err)
os.Exit(1)
Expand Down Expand Up @@ -155,7 +172,7 @@ func run(cmd *cobra.Command, args []string) {
// labels are recorded as each provider registers its tools.
mcpSrv.AddReceivingMiddleware(mcpserver.ToolMiddleware())

registerMCP(mcpSrv, tools, *kubeconfig, readOnly)
registerMCP(mcpSrv, tools, effectiveConfig.Kubeconfig, readOnly)

// Create wait group for server goroutines
var wg sync.WaitGroup
Expand Down
Loading
Loading