This is an automated email from the ASF dual-hosted git repository.
zhouzixin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-mcp.git
The following commit(s) were added to refs/heads/main by this push:
new 949a3d8 Add support mcp server for SkyWalking - stage1 (#1)
949a3d8 is described below
commit 949a3d8c6db484a0fa0e27c1985e6c1a3cfc22f3
Author: Zixin Zhou <[email protected]>
AuthorDate: Fri Jun 6 22:53:50 2025 +0800
Add support mcp server for SkyWalking - stage1 (#1)
---
.asf.yaml | 10 ++
.github/dependabot.yml | 28 ++++
.github/workflows/CI.yaml | 88 ++++++++++++
.github/workflows/publish-docker.yaml | 54 ++++++++
.golangci.yml | 76 +++++++++++
.licenserc.yaml | 39 ++++++
Dockerfile | 61 +++++++++
Makefile | 106 +++++++++++++++
README.md | 94 ++++++++++++-
cmd/skywalking-mcp/main.go | 97 ++++++++++++++
dist/LICENSE | 244 ++++++++++++++++++++++++++++++++++
dist/LICENSE.tpl | 14 ++
dist/NOTICE | 5 +
go.mod | 33 +++++
go.sum | 77 +++++++++++
internal/config/config.go | 70 ++++++++++
internal/swmcp/server.go | 60 +++++++++
internal/swmcp/sse.go | 123 +++++++++++++++++
internal/swmcp/stdio.go | 174 ++++++++++++++++++++++++
internal/tools/io.go | 62 +++++++++
internal/tools/tools.go | 102 ++++++++++++++
internal/tools/trace.go | 53 ++++++++
22 files changed, 1668 insertions(+), 2 deletions(-)
diff --git a/.asf.yaml b/.asf.yaml
index a71efcf..9de0674 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -29,3 +29,13 @@ github:
squash: true
merge: false
rebase: false
+ dependabot_updates: true
+ protected_branches:
+ main:
+ required_status_checks:
+ strict: true
+ contexts:
+ - Required
+ required_pull_request_reviews:
+ dismiss_stale_reviews: true
+ required_approving_review_count: 1
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..22a75f1
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,28 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+version: 2
+updates:
+ - package-ecosystem: "gomod"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ assignees:
+ - "CodePrometheus"
+ groups:
+ actions-deps:
+ patterns:
+ - "*"
diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml
new file mode 100644
index 0000000..3c662fc
--- /dev/null
+++ b/.github/workflows/CI.yaml
@@ -0,0 +1,88 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+name: CI
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+
+concurrency:
+ group: skywalking-mcp-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ check-license:
+ name: License header
+ if: github.repository == 'apache/skywalking-mcp'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Check License Header
+ uses: apache/skywalking-eyes@5dfa68f93380a5e57259faaf95088b7f133b5778
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: "1.24"
+
+ - name: Check Dependencies License
+ uses:
apache/skywalking-eyes/dependency@69f34abb75ec4e414b593ac3f34228b60e33f97b
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ flags: -w
+
+ build:
+ name: Build
+ runs-on: ubuntu-latest
+ if: github.repository == 'apache/skywalking-mcp'
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: 1.24
+
+ - name: Lint
+ run: make lint
+
+ - name: Build Docker images
+ run: make docker
+
+ required:
+ if: always()
+ name: Required
+ needs:
+ - check-license
+ - build
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - name: Merge Requirement
+ run: |
+ checkLicense=${{ needs.check-license.result }}
+ [[ ${checkLicense} == 'success' ]] || exit 1;
+ build=${{ needs.build.result }};
+ [[ ${build} == 'success' ]] || [[ ${build} == 'skipped' ]] || exit 3;
+ exit 0;
diff --git a/.github/workflows/publish-docker.yaml
b/.github/workflows/publish-docker.yaml
new file mode 100644
index 0000000..61e23f6
--- /dev/null
+++ b/.github/workflows/publish-docker.yaml
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: publish-docker
+
+on:
+ push:
+ branches:
+ - main
+
+env:
+ SKIP_TEST: true
+ HUB: ghcr.io/apache/skywalking-mcp
+
+jobs:
+ build:
+ if: github.repository == 'apache/skywalking-mcp'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+ timeout-minutes: 30
+ env:
+ VERSION: ${{ github.sha }}
+ APP_NAME: skywalking-mcp
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: true
+ - name: Set up Go
+ uses: actions/setup-go@v4
+ with:
+ go-version: 1.24
+ - name: Log in to the Container registry
+ uses: docker/login-action@v3
+ with:
+ registry: ${{ env.HUB }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Build and push docker images
+ run: make docker.push || make docker.push
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..2e10981
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,76 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+linters-settings:
+ govet:
+ enable:
+ - shadow
+ gocyclo:
+ min-complexity: 15
+ dupl:
+ threshold: 200
+ goconst:
+ min-len: 2
+ min-occurrences: 2
+ misspell:
+ locale: US
+ lll:
+ line-length: 150
+ goimports:
+ local-prefixes: github.com/apache/skywalking-mcp
+ gocritic:
+ enabled-tags:
+ - diagnostic
+ - experimental
+ - opinionated
+ - performance
+ - style
+ disabled-checks:
+ - ifElseChain
+ funlen:
+ lines: 100
+ statements: 50
+ whitespace:
+ multi-if: false
+ multi-func: false
+
+linters:
+ enable:
+ - bodyclose
+ - dogsled
+ - dupl
+ - errcheck
+ - funlen
+ - goconst
+ - gocritic
+ - gocyclo
+ - gofmt
+ - goimports
+ - revive
+ - gosec
+ - gosimple
+ - govet
+ - ineffassign
+ - lll
+ - misspell
+ - nakedret
+ - staticcheck
+ - stylecheck
+ - typecheck
+ - unconvert
+ - unparam
+ - unused
+ - whitespace
diff --git a/.licenserc.yaml b/.licenserc.yaml
new file mode 100644
index 0000000..945b96a
--- /dev/null
+++ b/.licenserc.yaml
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+header:
+ license:
+ spdx-id: Apache-2.0
+ copyright-owner: Apache Software Foundation
+
+ paths-ignore:
+ - "**/*.md"
+ - "dist"
+ - "licenses"
+ - "**/go.mod"
+ - "**/go.sum"
+ - "LICENSE"
+ - "NOTICE"
+ - ".gitignore"
+
+ comment: on-failure
+
+dependency:
+ files:
+ - go.mod
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..3e67b72
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,61 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Build stage
+FROM golang:1.24-bullseye AS builder
+
+# Default version
+ARG VERSION="dev"
+
+# Set the working directory
+WORKDIR /app
+
+# Copy go.mod and go.sum files
+COPY go.mod go.sum ./
+
+# Go get dependencies
+RUN go mod tidy
+
+# Copy the source code
+COPY . .
+
+# Build the application
+RUN CGO_ENABLED=0 go build \
+ -ldflags="-s -w -X main.version=${VERSION} -X main.commit=$(git rev-parse
HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
+ -o bin/swmcp ./cmd/skywalking-mcp/main.go
+
+# Make a stage to run the app
+FROM debian:bullseye-slim
+
+# Install ca-certificates for HTTPS requests
+RUN apt-get update && apt-get install -y ca-certificates && rm -rf
/var/lib/apt/lists/*
+
+# Create a non-root user
+RUN useradd -r -u 1000 -m skywalking-mcp
+
+# Set the working directory
+WORKDIR /app
+
+# Copy the binary from the builder stage
+COPY --from=builder --chown=1000:1000 /app/bin/swmcp /app/
+
+# Use the non-root user
+USER skywalking-mcp
+
+# Expose the port the app runs on
+EXPOSE 8000
+
+# Run the application, defaulting to SSE transport
+ENTRYPOINT ["/app/swmcp", "stdio"]
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c33b19f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,106 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+VERSION_PATH=main
+VERSION ?= dev-$(shell git rev-parse --short HEAD)
+GIT_COMMIT=$(shell git rev-parse HEAD)
+BUILD_DATE=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
+MKDIR_P = mkdir -p
+
+GO_LINT = golangci-lint
+LICENSE_EYE = license-eye
+
+HUB ?= docker.io/apache
+APP_NAME = skywalking-mcp
+
+.PHONY: all
+all: build ;
+
+.PHONY: build
+build: ## Build the binary.
+ ${MKDIR_P} bin/
+ CGO_ENABLED=0 go build -ldflags "\
+ -X ${VERSION_PATH}.version=${VERSION} \
+ -X ${VERSION_PATH}.commit=${GIT_COMMIT} \
+ -X ${VERSION_PATH}.date=${BUILD_DATE}" \
+ -o bin/swmcp cmd/skywalking-mcp/main.go
+
+.PHONY: build-image
+build-image: ## Build the Docker image.
+ docker build -t skywalking-mcp:latest .
+
+$(GO_LINT):
+ @$(GO_LINT) version > /dev/null 2>&1 || go install
github.com/golangci/golangci-lint/cmd/[email protected]
+$(LICENSE_EYE):
+ @$(LICENSE_EYE) --version > /dev/null 2>&1 || go install
github.com/apache/skywalking-eyes/cmd/license-eye@latest
+
+.PHONY: lint
+lint: $(GO_LINT)
+ $(GO_LINT) run -v --timeout 5m ./...
+.PHONY: fix-lint
+fix-lint: $(GO_LINT)
+ $(GO_LINT) run -v --fix ./...
+
+.PHONY: license-header
+license-header: clean $(LICENSE_EYE)
+ @$(LICENSE_EYE) header check
+
+.PHONY: fix-license-header
+fix-license-header: clean $(LICENSE_EYE)
+ @$(LICENSE_EYE) header fix
+
+.PHONY: dependency-license
+dependency-license: clean $(LICENSE_EYE)
+ @$(LICENSE_EYE) dependency resolve --summary ./dist/LICENSE.tpl
--output ./dist/licenses || exit 1
+ @if [ ! -z "`git diff -U0 ./dist`" ]; then \
+ echo "LICENSE file is not updated correctly"; \
+ git diff -U0 ./dist; \
+ exit 1; \
+ fi
+
+.PHONY: fix-dependency-license
+fix-dependency-license: clean $(LICENSE_EYE)
+ @$(LICENSE_EYE) dependency resolve --summary ./dist/LICENSE.tpl
--output ./dist/licenses
+
+.PHONY: fix-license
+fix-license: fix-license-header fix-dependency-license
+
+.PHONY: fix
+fix: fix-lint fix-license
+
+.PHONY: clean
+clean:
+ -rm -rf bin
+ -rm -rf coverage.txt
+ -rm -rf *.tgz
+ -rm -rf *.tgz
+ -rm -rf *.asc
+ -rm -rf *.sha512
+ @go mod tidy &> /dev/null
+
+.PHONY: docker
+docker: PUSH_OR_LOAD = --load
+docker: PLATFORMS =
+
+.PHONY: docker.push
+docker.push: PUSH_OR_LOAD = --push
+docker.push: PLATFORMS = --platform linux/386,linux/amd64,linux/arm64
+
+docker docker.push:
+ docker buildx create --use --driver docker-container --name
skywalking_mcp > /dev/null 2>&1 || true
+ docker buildx build $(PUSH_OR_LOAD) $(PLATFORMS) --build-arg
VERSION=$(VERSION) . -t $(HUB)/$(APP_NAME):$(VERSION) -t
$(HUB)/$(APP_NAME):latest
+ docker buildx rm skywalking_mcp
\ No newline at end of file
diff --git a/README.md b/README.md
index bd82a70..3c73c89 100644
--- a/README.md
+++ b/README.md
@@ -3,11 +3,99 @@ Apache SkyWalking MCP
<img src="http://skywalking.apache.org/assets/logo.svg" alt="Sky Walking logo"
height="90px" align="right" />
-**SkyWalking-MCP**: Apache SkyWalking MCP Server.
+**SkyWalking-MCP**: A [Model Context Protocol][mcp] (MCP) server for
integrating AI agents with Skywalking OAP and the surrounding ecosystem.
**SkyWalking**: an APM(application performance monitor) system, especially
designed for
microservices, cloud native and container-based (Docker, Kubernetes, Mesos)
architectures.
+## Usage
+
+### From Source
+
+```bash
+# Clone the repository
+git clone https://github.com/apache/skywalking-mcp.git
+cd skywalking-mcp && go mod tidy
+
+# Build the project
+make
+```
+
+### Command-line Options
+
+```bash
+Usage:
+ swmcp [command]
+
+Available Commands:
+ completion Generate the autocompletion script for the specified shell
+ help Help about any command
+ sse Start SSE server
+ stdio Start stdio server
+
+Flags:
+ -h, --help help for swmcp
+ --log-command When true, log commands to the log file
+ --log-file string Path to log file
+ --log-level string Logging level (debug, info, warn, error) (default
"info")
+ --read-only Restrict the server to read-only operations
+ --sw-url string Specify the OAP URL to connect to (e.g.
http://localhost:12800)
+ -v, --version version for swmcp
+
+Use "swmcp [command] --help" for more information about a command.
+```
+
+You could start the MCP server with the following command:
+
+```bash
+# use stdio server
+bin/swmcp stdio --sw-url http://localhost:12800
+
+# or use SSE server
+bin/swmcp sse --sse-address localhost:8000 --base-path /mcp --sw-url
http://localhost:12800
+```
+
+### Usage with Cursor
+
+```json
+{
+ "mcpServers": {
+ "skywalking": {
+ "command": "swmcp stdio",
+ "args": [
+ "--sw-url",
+ "http://localhost:12800"
+ ]
+ }
+ }
+}
+```
+
+If using Docker:
+
+`make build-image` to build the Docker image, then configure the MCP server
like this:
+
+```json
+{
+ "mcpServers": {
+ "skywalking": {
+ "command": "docker",
+ "args": [
+ "run",
+ "--rm",
+ "-i",
+ "-e",
+ "SW_URL",
+ "skywalking-mcp:latest"
+ ],
+ "env": {
+ "SW_URL": "http://localhost:12800"
+ }
+ }
+ }
+}
+```
+
## Contact Us
* Submit [an issue](https://github.com/apache/skywalking/issues/new) by using
[MCP] as title prefix.
* Mail list: **[email protected]**. Mail to
`[email protected]`, follow the reply to subscribe the mail
list.
@@ -15,4 +103,6 @@ microservices, cloud native and container-based (Docker,
Kubernetes, Mesos) arch
* Twitter, [ASFSkyWalking](https://twitter.com/ASFSkyWalking)
## License
-[Apache 2.0 License.](/LICENSE)
\ No newline at end of file
+[Apache 2.0 License.](/LICENSE)
+
+[mcp]: https://modelcontextprotocol.io/
\ No newline at end of file
diff --git a/cmd/skywalking-mcp/main.go b/cmd/skywalking-mcp/main.go
new file mode 100644
index 0000000..0504f34
--- /dev/null
+++ b/cmd/skywalking-mcp/main.go
@@ -0,0 +1,97 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package main
+
+import (
+ "context"
+ "fmt"
+ "log/slog"
+ "os"
+ "os/signal"
+ "strings"
+ "syscall"
+
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+
+ "github.com/apache/skywalking-mcp/internal/swmcp"
+)
+
+var (
+ version = "version"
+ commit = "commit"
+ date = "date"
+
+ rootCmd = &cobra.Command{
+ Use: "swmcp",
+ Short: "Apache SkyWalking MCP Server.",
+ Long: `This is a server that implements the MCP protocol for
Apache SkyWalking.`,
+ Version: fmt.Sprintf("Version: %s\nCommit: %s\nBuild Date: %s",
version, commit, date),
+ }
+)
+
+func init() {
+ // Set the environment variable prefix
+ viper.SetEnvPrefix("SW")
+ // Enable environment variable reading
+ viper.AutomaticEnv()
+ // All fields with . or - will be replaced with _ for ENV vars
+ viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
+
+ rootCmd.SetVersionTemplate("{{.Short}}\n{{.Version}}\n")
+
+ // Add global Flags
+ rootCmd.PersistentFlags().String("sw-url", "", "Specify the OAP URL to
connect to (e.g. http://localhost:12800)")
+ rootCmd.PersistentFlags().String("log-level", "info", "Logging level
(debug, info, warn, error)")
+ rootCmd.PersistentFlags().Bool("read-only", false, "Restrict the server
to read-only operations")
+ rootCmd.PersistentFlags().Bool("log-command", false, "When true, log
commands to the log file")
+ rootCmd.PersistentFlags().String("log-file", "", "Path to log file")
+
+ // Bind flag to viper
+ _ = viper.BindPFlag("url", rootCmd.PersistentFlags().Lookup("sw-url"))
+ _ = viper.BindPFlag("log-level",
rootCmd.PersistentFlags().Lookup("log-level"))
+ _ = viper.BindPFlag("read-only",
rootCmd.PersistentFlags().Lookup("read-only"))
+ _ = viper.BindPFlag("log-command",
rootCmd.PersistentFlags().Lookup("log-command"))
+ _ = viper.BindPFlag("log-file",
rootCmd.PersistentFlags().Lookup("log-file"))
+
+ slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr,
&slog.HandlerOptions{
+ Level: parseLogLevel(viper.GetString("log-level")),
+ })))
+
+ _, stop := signal.NotifyContext(context.Background(), os.Interrupt,
syscall.SIGTERM)
+ defer stop()
+
+ // Add subcommands
+ rootCmd.AddCommand(swmcp.NewStdioServer())
+ rootCmd.AddCommand(swmcp.NewSSEServer())
+}
+
+func main() {
+ if err := rootCmd.Execute(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+}
+
+func parseLogLevel(level string) slog.Level {
+ var slogLevel slog.Level
+ if err := slogLevel.UnmarshalText([]byte(level)); err != nil {
+ return slog.LevelInfo
+ }
+ return slogLevel
+}
diff --git a/dist/LICENSE b/dist/LICENSE
new file mode 100644
index 0000000..dae8ad2
--- /dev/null
+++ b/dist/LICENSE
@@ -0,0 +1,244 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+========================================================================
+Apache-2.0 licenses
+========================================================================
+
+The following components are provided under the Apache-2.0 License. See
project link for details.
+The text of each license is also included at licenses/license-[project].txt.
+
+
+ github.com/apache/skywalking-cli v0.0.0-20250604010708-77b4c49e89c9
Apache-2.0
+ github.com/inconshreveable/mousetrap v1.1.0 Apache-2.0
+ github.com/machinebox/graphql v0.2.2 Apache-2.0
+ github.com/spf13/afero v1.14.0 Apache-2.0
+ github.com/spf13/cobra v1.9.1 Apache-2.0
+ skywalking.apache.org/repo/goapi v0.0.0-20250520033135-e237d585745f
Apache-2.0
+
+========================================================================
+BSD-2-Clause licenses
+========================================================================
+
+The following components are provided under the BSD-2-Clause License. See
project link for details.
+The text of each license is also included at licenses/license-[project].txt.
+
+
+ github.com/pkg/errors v0.9.1 BSD-2-Clause
+
+========================================================================
+BSD-3-Clause licenses
+========================================================================
+
+The following components are provided under the BSD-3-Clause License. See
project link for details.
+The text of each license is also included at licenses/license-[project].txt.
+
+
+ github.com/fsnotify/fsnotify v1.9.0 BSD-3-Clause
+ github.com/google/uuid v1.6.0 BSD-3-Clause
+ github.com/spf13/pflag v1.0.6 BSD-3-Clause
+ github.com/yosida95/uritemplate/v3 v3.0.2 BSD-3-Clause
+ golang.org/x/sys v0.33.0 BSD-3-Clause
+ golang.org/x/text v0.25.0 BSD-3-Clause
+
+========================================================================
+MIT licenses
+========================================================================
+
+The following components are provided under the MIT License. See project link
for details.
+The text of each license is also included at licenses/license-[project].txt.
+
+
+ github.com/go-viper/mapstructure/v2 v2.2.1 MIT
+ github.com/mark3labs/mcp-go v0.31.0 MIT
+ github.com/pelletier/go-toml/v2 v2.2.4 MIT
+ github.com/sagikazarmark/locafero v0.9.0 MIT
+ github.com/sirupsen/logrus v1.9.3 MIT
+ github.com/sourcegraph/conc v0.3.0 MIT
+ github.com/spf13/cast v1.9.1 MIT
+ github.com/spf13/viper v1.20.1 MIT
+ github.com/subosito/gotenv v1.6.0 MIT
+ go.uber.org/multierr v1.11.0 MIT
+
+========================================================================
+MIT and Apache-2.0 licenses
+========================================================================
+
+The following components are provided under the MIT and Apache-2.0 License.
See project link for details.
+The text of each license is also included at licenses/license-[project].txt.
+
+
+ gopkg.in/yaml.v3 v3.0.1 MIT and Apache-2.0
+
diff --git a/dist/LICENSE.tpl b/dist/LICENSE.tpl
new file mode 100644
index 0000000..fec71c0
--- /dev/null
+++ b/dist/LICENSE.tpl
@@ -0,0 +1,14 @@
+{{.LicenseContent }}
+
+{{- range .Groups }}
+========================================================================
+{{.LicenseID}} licenses
+========================================================================
+
+The following components are provided under the {{.LicenseID}} License. See
project link for details.
+The text of each license is also included at licenses/license-[project].txt.
+
+{{ range .Deps }}
+ {{ .Name }} {{ .Version }} {{ .LicenseID }}
+{{- end }}
+{{ end }}
diff --git a/dist/NOTICE b/dist/NOTICE
new file mode 100644
index 0000000..636e417
--- /dev/null
+++ b/dist/NOTICE
@@ -0,0 +1,5 @@
+Apache SkyWalking
+Copyright 2017-2025 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..3f0da89
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,33 @@
+module github.com/apache/skywalking-mcp
+
+go 1.24.3
+
+require (
+ github.com/apache/skywalking-cli v0.0.0-20250604010708-77b4c49e89c9
+ github.com/mark3labs/mcp-go v0.31.0
+ github.com/sirupsen/logrus v1.9.3
+ github.com/spf13/cobra v1.9.1
+ github.com/spf13/viper v1.20.1
+ skywalking.apache.org/repo/goapi v0.0.0-20250520033135-e237d585745f
+)
+
+require (
+ github.com/fsnotify/fsnotify v1.9.0 // indirect
+ github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/machinebox/graphql v0.2.2 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.4 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/sagikazarmark/locafero v0.9.0 // indirect
+ github.com/sourcegraph/conc v0.3.0 // indirect
+ github.com/spf13/afero v1.14.0 // indirect
+ github.com/spf13/cast v1.9.1 // indirect
+ github.com/spf13/pflag v1.0.6 // indirect
+ github.com/subosito/gotenv v1.6.0 // indirect
+ github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ golang.org/x/sys v0.33.0 // indirect
+ golang.org/x/text v0.25.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..9126bd8
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,77 @@
+github.com/apache/skywalking-cli v0.0.0-20250604010708-77b4c49e89c9
h1:loGzKlrRMY5r4qoCFA+cWSEvCP4hj1dnf+BzrGSDKQE=
+github.com/apache/skywalking-cli v0.0.0-20250604010708-77b4c49e89c9/go.mod
h1:5S2bH5p65WLXmFufyX5JM1btkx6ail8wqdSkjy0hb3I=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod
h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/davecgh/go-spew v1.1.0/go.mod
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/frankban/quicktest v1.14.6
h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod
h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.9.0
h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod
h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/go-viper/mapstructure/v2 v2.2.1
h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
+github.com/go-viper/mapstructure/v2 v2.2.1/go.mod
h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod
h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/inconshreveable/mousetrap v1.1.0
h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod
h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod
h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod
h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/machinebox/graphql v0.2.2
h1:dWKpJligYKhYKO5A2gvNhkJdQMNZeChZYyBbrZkBZfo=
+github.com/machinebox/graphql v0.2.2/go.mod
h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA=
+github.com/mark3labs/mcp-go v0.31.0
h1:4UxSV8aM770OPmTvaVe/b1rA2oZAjBMhGBfUgOGut+4=
+github.com/mark3labs/mcp-go v0.31.0/go.mod
h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
+github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
+github.com/matryer/is v1.4.0/go.mod
h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
+github.com/pelletier/go-toml/v2 v2.2.4
h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod
h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.9.0
h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod
h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sagikazarmark/locafero v0.9.0
h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
+github.com/sagikazarmark/locafero v0.9.0/go.mod
h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
+github.com/sirupsen/logrus v1.9.3
h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod
h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sourcegraph/conc v0.3.0
h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod
h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
+github.com/spf13/afero v1.14.0/go.mod
h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
+github.com/spf13/cast v1.9.1 h1:LxnjuHJpEjFUlBvGDef9duW2jIfjfm9a2f4tCgyvsEw=
+github.com/spf13/cast v1.9.1/go.mod
h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
+github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
+github.com/spf13/cobra v1.9.1/go.mod
h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
+github.com/spf13/viper v1.20.1/go.mod
h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
+github.com/stretchr/objx v0.1.0/go.mod
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.0/go.mod
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.10.0
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+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/go.mod
h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
+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=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod
h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
+golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
+golang.org/x/text v0.25.0/go.mod
h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15
h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod
h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+skywalking.apache.org/repo/goapi v0.0.0-20250520033135-e237d585745f
h1:of4GHiflH8hS8uabPidaHvuruM8uyY5u78G83IDI1Fc=
+skywalking.apache.org/repo/goapi v0.0.0-20250520033135-e237d585745f/go.mod
h1:rTNGn2QrS+p1i2OaIBxlwQ/VrDSDc7OwRk/iWV+mU0k=
diff --git a/internal/config/config.go b/internal/config/config.go
new file mode 100644
index 0000000..81a0f57
--- /dev/null
+++ b/internal/config/config.go
@@ -0,0 +1,70 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package config
+
+const (
+ DefaultSWURL = "http://localhost:12800/graphql"
+)
+
+// MCPServerConfig holds the application configuration.
+type MCPServerConfig struct {
+ // SkyWalking OAP URL to target for API requests (e.g. localhost:12800)
+ URL string
+
+ // ReadOnly indicates if we should only offer read-only tools
+ ReadOnly bool
+
+ // Path to the log file if not stderr
+ LogFilePath string
+}
+
+// StdioServerConfig holds the configuration for Stdio.
+type StdioServerConfig struct {
+ // SkyWalking OAP URL to target for API requests (e.g. localhost:12800)
+ URL string
+
+ // ReadOnly indicates if we should only offer read-only tools
+ ReadOnly bool
+
+ // Path to the log file if not stderr
+ LogFilePath string
+
+ // LogCommands indicates if we should log commands
+ LogCommands bool
+}
+
+// SSEServerConfig holds the configuration for Stdio.
+type SSEServerConfig struct {
+ // SkyWalking OAP URL to target for API requests (e.g. localhost:12800)
+ URL string
+
+ // ReadOnly indicates if we should only offer read-only tools
+ ReadOnly bool
+
+ // Path to the log file if not stderr
+ LogFilePath string
+
+ // LogCommands indicates if we should log commands
+ LogCommands bool
+
+ // The host and port to start the sse server on
+ Address string
+
+ // Base path for the sse server
+ BasePath string
+}
diff --git a/internal/swmcp/server.go b/internal/swmcp/server.go
new file mode 100644
index 0000000..07067cd
--- /dev/null
+++ b/internal/swmcp/server.go
@@ -0,0 +1,60 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package swmcp
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/mark3labs/mcp-go/server"
+ "github.com/sirupsen/logrus"
+
+ "github.com/apache/skywalking-mcp/internal/tools"
+)
+
+// newMcpServer creates a new MCP server instance,
+// and we can add various tools and capabilities to it.
+func newMcpServer() *server.MCPServer {
+ mcpServer := server.NewMCPServer(
+ "skywalking-mcp",
+ "0.1.0",
+ server.WithResourceCapabilities(true, true),
+ server.WithLogging())
+
+ tools.AddTraceTools(mcpServer)
+
+ return mcpServer
+}
+
+func initLogger(logFilePath string) (*logrus.Logger, error) {
+ if logFilePath == "" {
+ return logrus.New(), nil
+ }
+
+ file, err := os.OpenFile(logFilePath,
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
+ if err != nil {
+ return nil, fmt.Errorf("failed to open log file: %w", err)
+ }
+
+ logrusLogger := logrus.New()
+ logrusLogger.SetFormatter(&logrus.TextFormatter{})
+ logrusLogger.SetLevel(logrus.DebugLevel)
+ logrusLogger.SetOutput(file)
+
+ return logrusLogger, nil
+}
diff --git a/internal/swmcp/sse.go b/internal/swmcp/sse.go
new file mode 100644
index 0000000..8f184b6
--- /dev/null
+++ b/internal/swmcp/sse.go
@@ -0,0 +1,123 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package swmcp
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ "github.com/mark3labs/mcp-go/server"
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+
+ "github.com/apache/skywalking-mcp/internal/config"
+)
+
+func NewSSEServer() *cobra.Command {
+ sseCmd := &cobra.Command{
+ Use: "sse",
+ Short: "Start SSE server",
+ Long: `Start a server that listens for Server-Sent Events
(SSE) on the specified address.`,
+ RunE: func(_ *cobra.Command, _ []string) error {
+ sseServerConfig := config.SSEServerConfig{
+ Address: viper.GetString("sse-address"),
+ BasePath: viper.GetString("base-path"),
+ }
+
+ return runSSEServer(context.Background(),
&sseServerConfig)
+ },
+ }
+
+ // Add SSE server specific flags
+ sseCmd.Flags().String("sse-address", "localhost:8000",
+ "The host and port to start the sse server on")
+ sseCmd.Flags().String("base-path", "",
+ "Base path for the sse server")
+ _ = viper.BindPFlag("sse-address", sseCmd.Flags().Lookup("sse-address"))
+ _ = viper.BindPFlag("base-path", sseCmd.Flags().Lookup("base-path"))
+
+ return sseCmd
+}
+
+// runSSEServer starts a server that listens for Server-Sent Events (SSE) on
the specified address.
+func runSSEServer(ctx context.Context, cfg *config.SSEServerConfig) error {
+ ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
+ defer stop()
+
+ logger, err := initLogger(cfg.LogFilePath)
+ if err != nil {
+ return fmt.Errorf("failed to initialize logger: %w", err)
+ }
+
+ sseServer := server.NewSSEServer(
+ newMcpServer(),
+ server.WithStaticBasePath(cfg.BasePath),
+ server.WithSSEContextFunc(EnhanceHTTPContextFunc()),
+ )
+ ssePath := sseServer.CompleteSsePath()
+ log.Printf("Starting SkyWalking MCP server using SSE transport
listening on http://%s%s\n ", cfg.Address, ssePath)
+
+ errCh := make(chan error, 1)
+ go func() {
+ if err := sseServer.Start(cfg.Address); err != nil &&
!errors.Is(err, http.ErrServerClosed) {
+ errCh <- err // bubble up real crashes
+ }
+ }()
+
+ // Give the server a moment to start
+ time.Sleep(100 * time.Millisecond)
+
+ // Block until Ctrl-C or an internal error
+ select {
+ case <-ctx.Done():
+ // user hit Ctrl-C
+ _, _ = fmt.Fprintln(os.Stderr, "Received shutdown signal,
stopping server...")
+ case err := <-errCh:
+ // HTTP server crashed
+ return fmt.Errorf("sse server error: %w", err)
+ }
+
+ // Graceful shutdown
+ shCtx, cancel := context.WithTimeout(context.Background(),
10*time.Second)
+ defer cancel()
+
+ // First try to shut down the SSE server
+ if err := sseServer.Shutdown(shCtx); err != nil {
+ if !errors.Is(err, http.ErrServerClosed) {
+ logger.Errorf("Error shutting down SSE server: %v", err)
+ }
+ }
+
+ // Wait for any remaining operations to complete
+ select {
+ case <-shCtx.Done():
+ return fmt.Errorf("shutdown timed out")
+ case <-time.After(100 * time.Millisecond):
+ // Give a small grace period for cleanup
+ }
+
+ _, _ = fmt.Fprintln(os.Stderr, "SSE server stopped gracefully")
+ return nil
+}
diff --git a/internal/swmcp/stdio.go b/internal/swmcp/stdio.go
new file mode 100644
index 0000000..95c59e7
--- /dev/null
+++ b/internal/swmcp/stdio.go
@@ -0,0 +1,174 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package swmcp
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "log/slog"
+ "net/http"
+ "os"
+ "os/signal"
+ "strings"
+ "syscall"
+
+ "github.com/apache/skywalking-cli/pkg/contextkey"
+ "github.com/mark3labs/mcp-go/server"
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+
+ "github.com/apache/skywalking-mcp/internal/config"
+ "github.com/apache/skywalking-mcp/internal/tools"
+)
+
+func NewStdioServer() *cobra.Command {
+ return &cobra.Command{
+ Use: "stdio",
+ Short: "Start stdio server",
+ Long: `Start a server that communicates via standard
input/output streams using JSON-RPC messages.`,
+ RunE: func(_ *cobra.Command, _ []string) error {
+ url := viper.GetString("url")
+ if url == "" {
+ return errors.New("SW_URL must be specified")
+ }
+
+ stdioServerConfig := config.StdioServerConfig{
+ URL: url,
+ ReadOnly: viper.GetBool("read-only"),
+ LogFilePath: viper.GetString("log-file"),
+ LogCommands: viper.GetBool("log-command"),
+ }
+
+ return runStdioServer(context.Background(),
&stdioServerConfig)
+ },
+ }
+}
+
+// runStdioServer starts a standard input/output server for the MCP protocol.
+func runStdioServer(ctx context.Context, cfg *config.StdioServerConfig) error {
+ slog.Info("Start a server that communicates via standard input/output
streams using JSON-RPC messages.")
+ // Handle SIGINT and SIGTERM
+ ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
+ defer stop()
+
+ stdioServer := server.NewStdioServer(newMcpServer())
+
+ logger, err := initLogger(cfg.LogFilePath)
+ if err != nil {
+ return fmt.Errorf("failed to initialize logger: %w", err)
+ }
+
+ stdLogger := log.New(logger.Writer(), "swmcp-stdio-server", 0)
+ stdioServer.SetErrorLogger(stdLogger)
+ stdioServer.SetContextFunc(EnhanceStdioContextFunc())
+
+ // Start listening for messages
+ errC := make(chan error, 1)
+ go func() {
+ in, out := io.Reader(os.Stdin), io.Writer(os.Stdout)
+
+ if cfg.LogCommands {
+ loggedIO := tools.NewIOLogger(in, out, logger)
+ in, out = loggedIO, loggedIO
+ }
+
+ errC <- stdioServer.Listen(ctx, in, out)
+ }()
+
+ // Output github-mcp-server string
+ _, _ = fmt.Fprintf(os.Stderr, "SkyWalking MCP Server running on
stdio\n")
+
+ // Wait for shutdown signal
+ select {
+ case <-ctx.Done():
+ logger.Infof("shutting down server...")
+ case err := <-errC:
+ if err != nil {
+ return fmt.Errorf("error running server: %w", err)
+ }
+ }
+
+ return nil
+}
+
+var ExtractSWURLFromCfg server.StdioContextFunc = func(ctx context.Context)
context.Context {
+ urlStr := viper.GetString("url")
+ if urlStr == "" {
+ urlStr = config.DefaultSWURL
+ }
+
+ // we need to ensure the URL ends with "/graphql"
+ if !strings.HasSuffix(urlStr, "/graphql") {
+ urlStr = strings.TrimRight(urlStr, "/") + "/graphql"
+ }
+ return WithSkyWalkingURLAndInsecure(ctx, urlStr)
+}
+
+var ExtractSWURLFromHeaders server.SSEContextFunc = func(ctx context.Context,
req *http.Request) context.Context {
+ urlStr := req.Header.Get("SW-URL")
+ if urlStr == "" {
+ urlStr = viper.GetString("url")
+ if urlStr == "" {
+ urlStr = config.DefaultSWURL
+ }
+ }
+
+ // we need to ensure the URL ends with "/graphql"
+ if !strings.HasSuffix(urlStr, "/graphql") {
+ urlStr = strings.TrimRight(urlStr, "/") + "/graphql"
+ }
+ return WithSkyWalkingURLAndInsecure(ctx, urlStr)
+}
+
+func EnhanceStdioContextFuncs(funcs ...server.StdioContextFunc)
server.StdioContextFunc {
+ return func(ctx context.Context) context.Context {
+ for _, f := range funcs {
+ ctx = f(ctx)
+ }
+ return ctx
+ }
+}
+
+func EnhanceSSEContextFuncs(funcs ...server.SSEContextFunc)
server.SSEContextFunc {
+ return func(ctx context.Context, r *http.Request) context.Context {
+ for _, f := range funcs {
+ ctx = f(ctx, r)
+ }
+ return ctx
+ }
+}
+
+// WithSkyWalkingURLAndInsecure adds the SkyWalking URL and Insecure to the
context.
+func WithSkyWalkingURLAndInsecure(ctx context.Context, url string)
context.Context {
+ ctx = context.WithValue(ctx, contextkey.BaseURL{}, url)
+ ctx = context.WithValue(ctx, contextkey.Insecure{}, false)
+ return ctx
+}
+
+// EnhanceStdioContextFunc returns a StdioContextFunc that composes all the
provided StdioContextFuncs.
+func EnhanceStdioContextFunc() server.StdioContextFunc {
+ return EnhanceStdioContextFuncs(ExtractSWURLFromCfg)
+}
+
+// EnhanceHTTPContextFunc returns a SSEContextFunc that composes all the
provided HTTPContextFuncs.
+func EnhanceHTTPContextFunc() server.SSEContextFunc {
+ return EnhanceSSEContextFuncs(ExtractSWURLFromHeaders)
+}
diff --git a/internal/tools/io.go b/internal/tools/io.go
new file mode 100644
index 0000000..3eeaee0
--- /dev/null
+++ b/internal/tools/io.go
@@ -0,0 +1,62 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package tools
+
+import (
+ "io"
+
+ log "github.com/sirupsen/logrus"
+)
+
+// IOLogger is a wrapper around io.Reader and io.Writer that can be used
+// to log the data being read and written from the underlying streams
+type IOLogger struct {
+ reader io.Reader
+ writer io.Writer
+ logger *log.Logger
+}
+
+// NewIOLogger creates a new IOLogger instance
+func NewIOLogger(r io.Reader, w io.Writer, logger *log.Logger) *IOLogger {
+ return &IOLogger{
+ reader: r,
+ writer: w,
+ logger: logger,
+ }
+}
+
+// Read reads data from the underlying io.Reader and logs it.
+func (l *IOLogger) Read(p []byte) (n int, err error) {
+ if l.reader == nil {
+ return 0, io.EOF
+ }
+ n, err = l.reader.Read(p)
+ if n > 0 {
+ l.logger.Infof("[stdin]: received %d bytes: %s", n,
string(p[:n]))
+ }
+ return n, err
+}
+
+// Write writes data to the underlying io.Writer and logs it.
+func (l *IOLogger) Write(p []byte) (n int, err error) {
+ if l.writer == nil {
+ return 0, io.ErrClosedPipe
+ }
+ l.logger.Infof("[stdout]: sending %d bytes: %s", len(p), string(p))
+ return l.writer.Write(p)
+}
diff --git a/internal/tools/tools.go b/internal/tools/tools.go
new file mode 100644
index 0000000..c491fd9
--- /dev/null
+++ b/internal/tools/tools.go
@@ -0,0 +1,102 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package tools
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "github.com/mark3labs/mcp-go/mcp"
+ "github.com/mark3labs/mcp-go/server"
+)
+
+type Tool[T any, R any] struct {
+ Name string
+ Description string
+ Handler func(ctx context.Context, args T) (R, error)
+ Options []mcp.ToolOption
+}
+
+func NewTool[T any, R any](
+ name, desc string,
+ handler func(ctx context.Context, args T) (R, error),
+ options ...mcp.ToolOption,
+) *Tool[T, R] {
+ return &Tool[T, R]{
+ Name: name,
+ Description: desc,
+ Handler: handler,
+ Options: options,
+ }
+}
+
+// Register registers the tool with the given MCP server.
+func (t *Tool[T, R]) Register(server *server.MCPServer) {
+ tool, handler, err := ConvertTool[T, R](t.Name, t.Description,
t.Handler, t.Options...)
+ if err != nil {
+ panic(err)
+ }
+
+ server.AddTool(tool, handler)
+}
+
+func ConvertTool[T any, R any](
+ name string,
+ desc string,
+ handlerFunc func(ctx context.Context, args T) (R, error),
+ options ...mcp.ToolOption,
+) (mcp.Tool, server.ToolHandlerFunc, error) {
+ baseOptions := []mcp.ToolOption{
+ mcp.WithDescription(desc),
+ mcp.WithTitleAnnotation(name),
+ mcp.WithIdempotentHintAnnotation(true), // we assume tools are
idempotent by default
+ }
+
+ baseOptions = append(baseOptions, options...)
+ tool := mcp.NewTool(name, baseOptions...)
+
+ handler := func(ctx context.Context, request mcp.CallToolRequest)
(*mcp.CallToolResult, error) {
+ var args T
+ if err := request.BindArguments(&args); err != nil {
+ return nil, fmt.Errorf("failed to bind arguments: %w",
err)
+ }
+
+ result, err := handlerFunc(ctx, args)
+ if err != nil {
+ return nil, err
+ }
+
+ switch v := any(result).(type) {
+ case *mcp.CallToolResult:
+ return v, nil
+ case mcp.CallToolResult:
+ return &v, nil
+ case nil:
+ return nil, nil
+ default:
+ jsonBytes, err := json.Marshal(v)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal
return value: %s", err)
+ }
+ return mcp.NewToolResultText(string(jsonBytes)), nil
+ }
+ }
+
+ return tool, handler, nil
+}
diff --git a/internal/tools/trace.go b/internal/tools/trace.go
new file mode 100644
index 0000000..5108d95
--- /dev/null
+++ b/internal/tools/trace.go
@@ -0,0 +1,53 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package tools
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/apache/skywalking-cli/pkg/graphql/trace"
+ "github.com/mark3labs/mcp-go/mcp"
+ "github.com/mark3labs/mcp-go/server"
+ api "skywalking.apache.org/repo/goapi/query"
+)
+
+type TraceRequest struct {
+ TraceID string `json:"trace_id"`
+}
+
+func searchTrace(ctx context.Context, req TraceRequest) (*api.Trace, error) {
+ traces, err := trace.Trace(ctx, req.TraceID)
+ if err != nil {
+ return nil, fmt.Errorf("search trace %v failed: %w",
req.TraceID, err)
+ }
+ return &traces, nil
+}
+
+func AddTraceTools(mcp *server.MCPServer) {
+ SearchTraceTool.Register(mcp)
+}
+
+var SearchTraceTool = NewTool[TraceRequest, *api.Trace](
+ "search_trace_by_trace_id",
+ "Search for traces by a single TraceId",
+ searchTrace,
+ mcp.WithTitleAnnotation("Search a trace by TraceId"),
+ mcp.WithString("trace_id", mcp.Required(),
+ mcp.Description("The TraceId to search for")),
+)