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")),
+)

Reply via email to