diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a7afd66..b76696c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -20,10 +20,6 @@ updates: directory: "/" schedule: interval: "daily" - reviewers: - - "CodePrometheus" - assignees: - - "CodePrometheus" ignore: - dependency-name: "github.com/apache/skywalking-cli" groups: diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 3c662fc..6cd622e 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -44,7 +44,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.24" + go-version: "1.25" - name: Check Dependencies License uses: apache/skywalking-eyes/dependency@69f34abb75ec4e414b593ac3f34228b60e33f97b @@ -62,7 +62,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.24 + go-version: 1.25 - name: Lint run: make lint diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index 61e23f6..283b2b6 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -41,9 +41,9 @@ jobs: with: submodules: true - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: - go-version: 1.24 + go-version: 1.25 - name: Log in to the Container registry uses: docker/login-action@v3 with: diff --git a/.gitignore b/.gitignore index b70ba09..4f34813 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,7 @@ .DS_Store coverage.txt target/ +inspector-config.json +*.log +.vscode/ +.claude/ diff --git a/Dockerfile b/Dockerfile index 3e67b72..80eedcc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ # limitations under the License. # Build stage -FROM golang:1.24-bullseye AS builder +FROM golang:1.25-bookworm AS builder # Default version ARG VERSION="dev" @@ -37,7 +37,7 @@ RUN CGO_ENABLED=0 go build \ -o bin/swmcp ./cmd/skywalking-mcp/main.go # Make a stage to run the app -FROM debian:bullseye-slim +FROM debian:bookworm-slim # Install ca-certificates for HTTPS requests RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* diff --git a/README.md b/README.md index a07b904..908331b 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ bin/swmcp stdio --sw-url http://localhost:12800 bin/swmcp sse --sse-address localhost:8000 --base-path /mcp --sw-url http://localhost:12800 ``` -### Usage with Cursor +### Usage with Cursor, Copilot, Claude Code ```json { @@ -86,13 +86,10 @@ If using Docker: "run", "--rm", "-i", - "-e", - "SW_URL", - "skywalking-mcp:latest" - ], - "env": { - "SW_URL": "http://localhost:12800" - } + "skywalking-mcp:latest", + "--sw-url", + "http://localhost:12800" + ] } } } @@ -102,17 +99,41 @@ If using Docker: SkyWalking MCP provides the following tools to query and analyze SkyWalking OAP data: -| Category | Tool Name | Description | Key Features | -|-------------|--------------------------|----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **Trace** | `get_trace_details` | Get detailed trace information | Retrieve trace by ID; **Multiple views**: `full` (complete trace), `summary` (overview with metrics), `errors_only` (error spans only); Detailed span analysis | -| **Trace** | `get_cold_trace_details` | Get trace details from cold storage | Query historical traces from BanyanDB; **Multiple views**: `full`, `summary`, `errors_only`; Duration-based search; Historical incident investigation | -| **Trace** | `query_traces` | Query traces with intelligent analysis | Multi-condition filtering (service, endpoint, duration, state, tags); **Multiple views**: `full` (raw data), `summary` (intelligent analysis with performance insights), `errors_only` (error traces); Sort options; Slow trace detection; Performance metrics and statistics | -| **Metrics** | `query_single_metrics` | Query single metric values | Get specific metric values (CPM, response time, SLA, Apdex); Multiple entity scopes (Service, ServiceInstance, Endpoint, Process, Relations); Time range and cold storage support | -| **Metrics** | `query_top_n_metrics` | Query top N metric rankings | Rank entities by metric values; Configurable top N count; Ascending/descending order; Scope-based filtering; Performance analysis and issue identification | -| **Log** | `query_logs` | Query logs from SkyWalking OAP | Filter by service, instance, endpoint, trace ID, tags; Time range queries; Cold storage support; Pagination support | -| **MQE** | `execute_mqe_expression` | Execute MQE expressions for metrics | Execute complex MQE (Metrics Query Expression) queries; Support calculations, aggregations, comparisons, TopN, trend analysis; Multiple result types (single value, time series, sorted list); Entity filtering and relation metrics; Debug and tracing capabilities | -| **MQE** | `list_mqe_metrics` | List available metrics for MQE | Discover available metrics for MQE queries; Filter by regex patterns; Get metric metadata (type, catalog); Support service, instance, endpoint, relation, database, and infrastructure metrics | -| **MQE** | `get_mqe_metric_type` | Get metric type information | Get detailed type information for specific metrics; Understand metric structure (regular value, labeled value, sampled record); Help with correct MQE expression syntax | +| Category | Tool Name | Description | +|--------------|--------------------------------|---------------------------------------------------------------------------------------------------| +| **Trace** | `query_traces` | Query traces with multi-condition filtering (service, endpoint, state, tags, and time range via start/end/step). Supports `full`, `summary`, and `errors_only` views with performance insights. | +| **Log** | `query_logs` | Query logs with filters for service, instance, endpoint, trace ID, tags, and time range. Supports cold storage and pagination. | +| **MQE** | `execute_mqe_expression` | Execute MQE (Metrics Query Expression) to query and calculate metrics data. Supports calculations, aggregations, TopN, trend analysis, and multiple result types. | +| **MQE** | `list_mqe_metrics` | List available metrics for MQE queries. Filter by regex pattern; returns metric name, type, and catalog. | +| **MQE** | `get_mqe_metric_type` | Get type information (REGULAR_VALUE, LABELED_VALUE, SAMPLED_RECORD) for a specific metric to help build correct MQE expressions. | +| **Metadata** | `list_layers` | List all layers registered in SkyWalking OAP (e.g. GENERAL, MESH, K8S). | +| **Metadata** | `list_services` | List all services registered in SkyWalking OAP under a specific layer. | +| **Metadata** | `list_instances` | List all instances of a service (e.g. pods or JVM processes). | +| **Metadata** | `list_endpoints` | List endpoints of a service with optional keyword filtering. | +| **Metadata** | `list_processes` | List processes of a service instance. | +| **Event** | `query_events` | Query events (deployments, restarts, scaling) with filters for service, instance, endpoint, type, and layer. | +| **Alarm** | `query_alarms` | Query alarms triggered by metric threshold breaches. Filter by scope, keyword, and tags. | +| **Topology** | `query_services_topology` | Query global or scoped service topology. Optionally filter by specific service IDs or layer. | +| **Topology** | `query_instances_topology` | Query service instance topology between a client service and a server service. | +| **Topology** | `query_endpoints_topology` | Query endpoint dependency topology for a given endpoint. | +| **Topology** | `query_processes_topology` | Query process topology for a given service instance. | + +## Available Prompts + +SkyWalking MCP provides the following prompts for guided analysis workflows: + +| Category | Prompt Name | Description | Arguments | +|-----------------|------------------------------|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------| +| **Performance** | `analyze-performance` | Analyze service performance using metrics tools | `service_name` (required), `start` (optional), `end` (optional) | +| **Performance** | `compare-services` | Compare performance metrics between multiple services | `services` (required), `metrics` (optional), `start` (optional), `end` (optional) | +| **Performance** | `top-services` | Find top N services ranked by a given metric | `metric_name` (required), `top_n` (optional), `order` (optional) | +| **Trace** | `investigate-traces` | Investigate traces for errors and performance issues | `service_id` (optional), `trace_state` (optional), `start` (optional), `end` (optional) | +| **Trace** | `trace-deep-dive` | Deep dive analysis of a specific trace | `trace_id` (required), `view` (optional) | +| **Log** | `analyze-logs` | Analyze service logs for errors and patterns | `service_id` (optional), `log_level` (optional), `start` (optional), `end` (optional) | +| **Topology** | `explore-service-topology` | Explore services, instances, endpoints, and processes within a layer and time range | `layer` (required), `start` (required), `end` (optional) | +| **MQE** | `build-mqe-query` | Help build MQE expressions for complex metric queries | `query_type` (required), `metrics` (required), `conditions` (optional) | +| **MQE** | `explore-metrics` | Explore available metrics and their types | `pattern` (optional), `show_examples` (optional) | +| **Utility** | `generate_duration` | Convert a natural-language time range into a `{start, end}` duration object for use with other tools | `time_range` (required) | ## Contact Us diff --git a/dist/LICENSE b/dist/LICENSE index dae8ad2..f1767eb 100644 --- a/dist/LICENSE +++ b/dist/LICENSE @@ -181,12 +181,13 @@ The following components are provided under the Apache-2.0 License. See project 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/apache/skywalking-cli v0.0.0-20251013114703-9a1beab08413 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 + github.com/spf13/afero v1.15.0 Apache-2.0 + github.com/spf13/cobra v1.10.2 Apache-2.0 + github.com/wk8/go-ordered-map/v2 v2.1.8 Apache-2.0 + skywalking.apache.org/repo/goapi v0.0.0-20251021000546-17778a1a5d70 Apache-2.0 ======================================================================== BSD-2-Clause licenses @@ -197,6 +198,7 @@ The text of each license is also included at licenses/license-[project].txt. github.com/pkg/errors v0.9.1 BSD-2-Clause + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 BSD-2-Clause ======================================================================== BSD-3-Clause licenses @@ -206,12 +208,13 @@ The following components are provided under the BSD-3-Clause License. See projec The text of each license is also included at licenses/license-[project].txt. + github.com/bahlo/generic-list-go v0.2.0 BSD-3-Clause 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/spf13/pflag v1.0.10 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 + golang.org/x/sys v0.42.0 BSD-3-Clause + golang.org/x/text v0.34.0 BSD-3-Clause ======================================================================== MIT licenses @@ -221,16 +224,17 @@ The following components are provided under the MIT License. See project link fo 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/buger/jsonparser v1.1.1 MIT + github.com/go-viper/mapstructure/v2 v2.5.0 MIT + github.com/invopop/jsonschema v0.13.0 MIT + github.com/mailru/easyjson v0.9.1 MIT + github.com/mark3labs/mcp-go v0.45.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/sagikazarmark/locafero v0.12.0 MIT + github.com/sirupsen/logrus v1.9.4 MIT + github.com/spf13/cast v1.10.0 MIT + github.com/spf13/viper v1.21.0 MIT github.com/subosito/gotenv v1.6.0 MIT - go.uber.org/multierr v1.11.0 MIT ======================================================================== MIT and Apache-2.0 licenses @@ -240,5 +244,6 @@ The following components are provided under the MIT and Apache-2.0 License. See The text of each license is also included at licenses/license-[project].txt. + go.yaml.in/yaml/v3 v3.0.4 MIT and Apache-2.0 gopkg.in/yaml.v3 v3.0.1 MIT and Apache-2.0 diff --git a/dist/licenses/license-github.com-apache-skywalking-cli.txt b/dist/licenses/license-github.com-apache-skywalking-cli.txt new file mode 100644 index 0000000..5a80c2b --- /dev/null +++ b/dist/licenses/license-github.com-apache-skywalking-cli.txt @@ -0,0 +1,210 @@ + 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. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed 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. + +======================================================================== +Apache 2.0 licenses +======================================================================== + +The following components are provided under the Apache License. See project link for details. +The text of each license is the standard Apache 2.0 license. + + pkg/display/graph/flamegraph files from jvm-profiling-tools/async-profiler: https://github.com/jvm-profiling-tools/async-profiler Apache-2.0 \ No newline at end of file diff --git a/dist/licenses/license-github.com-bahlo-generic-list-go.txt b/dist/licenses/license-github.com-bahlo-generic-list-go.txt new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/dist/licenses/license-github.com-bahlo-generic-list-go.txt @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/licenses/license-github.com-buger-jsonparser.txt b/dist/licenses/license-github.com-buger-jsonparser.txt new file mode 100644 index 0000000..ac25aeb --- /dev/null +++ b/dist/licenses/license-github.com-buger-jsonparser.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Leonid Bugaev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dist/licenses/license-github.com-fsnotify-fsnotify.txt b/dist/licenses/license-github.com-fsnotify-fsnotify.txt new file mode 100644 index 0000000..fb03ade --- /dev/null +++ b/dist/licenses/license-github.com-fsnotify-fsnotify.txt @@ -0,0 +1,25 @@ +Copyright © 2012 The Go Authors. All rights reserved. +Copyright © fsnotify Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +* Neither the name of Google Inc. nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/licenses/license-github.com-go-viper-mapstructure-v2.txt b/dist/licenses/license-github.com-go-viper-mapstructure-v2.txt new file mode 100644 index 0000000..f9c841a --- /dev/null +++ b/dist/licenses/license-github.com-go-viper-mapstructure-v2.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/dist/licenses/license-github.com-google-uuid.txt b/dist/licenses/license-github.com-google-uuid.txt new file mode 100644 index 0000000..5dc6826 --- /dev/null +++ b/dist/licenses/license-github.com-google-uuid.txt @@ -0,0 +1,27 @@ +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/licenses/license-github.com-inconshreveable-mousetrap.txt b/dist/licenses/license-github.com-inconshreveable-mousetrap.txt new file mode 100644 index 0000000..5f920e9 --- /dev/null +++ b/dist/licenses/license-github.com-inconshreveable-mousetrap.txt @@ -0,0 +1,201 @@ + 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. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Alan Shreve (@inconshreveable) + + Licensed 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. diff --git a/dist/licenses/license-github.com-invopop-jsonschema.txt b/dist/licenses/license-github.com-invopop-jsonschema.txt new file mode 100644 index 0000000..2993ec0 --- /dev/null +++ b/dist/licenses/license-github.com-invopop-jsonschema.txt @@ -0,0 +1,19 @@ +Copyright (C) 2014 Alec Thomas + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dist/licenses/license-github.com-machinebox-graphql.txt b/dist/licenses/license-github.com-machinebox-graphql.txt new file mode 100644 index 0000000..9b0dfaa --- /dev/null +++ b/dist/licenses/license-github.com-machinebox-graphql.txt @@ -0,0 +1,201 @@ + 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. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 Machine Box, Ltd. + + Licensed 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. diff --git a/dist/licenses/license-github.com-mailru-easyjson.txt b/dist/licenses/license-github.com-mailru-easyjson.txt new file mode 100644 index 0000000..fbff658 --- /dev/null +++ b/dist/licenses/license-github.com-mailru-easyjson.txt @@ -0,0 +1,7 @@ +Copyright (c) 2016 Mail.Ru Group + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/dist/licenses/license-github.com-mark3labs-mcp-go.txt b/dist/licenses/license-github.com-mark3labs-mcp-go.txt new file mode 100644 index 0000000..3d48435 --- /dev/null +++ b/dist/licenses/license-github.com-mark3labs-mcp-go.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Anthropic, PBC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dist/licenses/license-github.com-pelletier-go-toml-v2.txt b/dist/licenses/license-github.com-pelletier-go-toml-v2.txt new file mode 100644 index 0000000..991e2ae --- /dev/null +++ b/dist/licenses/license-github.com-pelletier-go-toml-v2.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +go-toml v2 +Copyright (c) 2021 - 2023 Thomas Pelletier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dist/licenses/license-github.com-pkg-errors.txt b/dist/licenses/license-github.com-pkg-errors.txt new file mode 100644 index 0000000..835ba3e --- /dev/null +++ b/dist/licenses/license-github.com-pkg-errors.txt @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/licenses/license-github.com-sagikazarmark-locafero.txt b/dist/licenses/license-github.com-sagikazarmark-locafero.txt new file mode 100644 index 0000000..a70b0f2 --- /dev/null +++ b/dist/licenses/license-github.com-sagikazarmark-locafero.txt @@ -0,0 +1,19 @@ +Copyright (c) 2023 Márk Sági-Kazár + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/dist/licenses/license-github.com-sirupsen-logrus.txt b/dist/licenses/license-github.com-sirupsen-logrus.txt new file mode 100644 index 0000000..f090cb4 --- /dev/null +++ b/dist/licenses/license-github.com-sirupsen-logrus.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Simon Eskildsen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/dist/licenses/license-github.com-spf13-afero.txt b/dist/licenses/license-github.com-spf13-afero.txt new file mode 100644 index 0000000..298f0e2 --- /dev/null +++ b/dist/licenses/license-github.com-spf13-afero.txt @@ -0,0 +1,174 @@ + 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. diff --git a/dist/licenses/license-github.com-spf13-cast.txt b/dist/licenses/license-github.com-spf13-cast.txt new file mode 100644 index 0000000..4527efb --- /dev/null +++ b/dist/licenses/license-github.com-spf13-cast.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Steve Francia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/dist/licenses/license-github.com-spf13-cobra.txt b/dist/licenses/license-github.com-spf13-cobra.txt new file mode 100644 index 0000000..298f0e2 --- /dev/null +++ b/dist/licenses/license-github.com-spf13-cobra.txt @@ -0,0 +1,174 @@ + 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. diff --git a/dist/licenses/license-github.com-spf13-pflag.txt b/dist/licenses/license-github.com-spf13-pflag.txt new file mode 100644 index 0000000..63ed1cf --- /dev/null +++ b/dist/licenses/license-github.com-spf13-pflag.txt @@ -0,0 +1,28 @@ +Copyright (c) 2012 Alex Ogier. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/licenses/license-github.com-spf13-viper.txt b/dist/licenses/license-github.com-spf13-viper.txt new file mode 100644 index 0000000..4527efb --- /dev/null +++ b/dist/licenses/license-github.com-spf13-viper.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Steve Francia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/dist/licenses/license-github.com-subosito-gotenv.txt b/dist/licenses/license-github.com-subosito-gotenv.txt new file mode 100644 index 0000000..f64ccae --- /dev/null +++ b/dist/licenses/license-github.com-subosito-gotenv.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Alif Rachmawadi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/dist/licenses/license-github.com-wk8-go-ordered-map-v2.txt b/dist/licenses/license-github.com-wk8-go-ordered-map-v2.txt new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/dist/licenses/license-github.com-wk8-go-ordered-map-v2.txt @@ -0,0 +1,201 @@ + 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. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed 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. diff --git a/dist/licenses/license-github.com-yosida95-uritemplate-v3.txt b/dist/licenses/license-github.com-yosida95-uritemplate-v3.txt new file mode 100644 index 0000000..79e8f87 --- /dev/null +++ b/dist/licenses/license-github.com-yosida95-uritemplate-v3.txt @@ -0,0 +1,25 @@ +Copyright (C) 2016, Kohei YOSHIDA . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/licenses/license-go.yaml.in-yaml-v3.txt b/dist/licenses/license-go.yaml.in-yaml-v3.txt new file mode 100644 index 0000000..2683e4b --- /dev/null +++ b/dist/licenses/license-go.yaml.in-yaml-v3.txt @@ -0,0 +1,50 @@ + +This project is covered by two different licenses: MIT and Apache. + +#### MIT License #### + +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original MIT license, with the additional +copyright staring in 2011 when the project was ported over: + + apic.go emitterc.go parserc.go readerc.go scannerc.go + writerc.go yamlh.go yamlprivateh.go + +Copyright (c) 2006-2010 Kirill Simonov +Copyright (c) 2006-2011 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +### Apache License ### + +All the remaining project files are covered by the Apache license: + +Copyright (c) 2011-2019 Canonical Ltd + +Licensed 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. diff --git a/dist/licenses/license-golang.org-x-sys.txt b/dist/licenses/license-golang.org-x-sys.txt new file mode 100644 index 0000000..2a7cf70 --- /dev/null +++ b/dist/licenses/license-golang.org-x-sys.txt @@ -0,0 +1,27 @@ +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/licenses/license-golang.org-x-text.txt b/dist/licenses/license-golang.org-x-text.txt new file mode 100644 index 0000000..2a7cf70 --- /dev/null +++ b/dist/licenses/license-golang.org-x-text.txt @@ -0,0 +1,27 @@ +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/licenses/license-gopkg.in-check.v1.txt b/dist/licenses/license-gopkg.in-check.v1.txt new file mode 100644 index 0000000..545cf2d --- /dev/null +++ b/dist/licenses/license-gopkg.in-check.v1.txt @@ -0,0 +1,25 @@ +Gocheck - A rich testing framework for Go + +Copyright (c) 2010-2013 Gustavo Niemeyer + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/licenses/license-gopkg.in-yaml.v3.txt b/dist/licenses/license-gopkg.in-yaml.v3.txt new file mode 100644 index 0000000..2683e4b --- /dev/null +++ b/dist/licenses/license-gopkg.in-yaml.v3.txt @@ -0,0 +1,50 @@ + +This project is covered by two different licenses: MIT and Apache. + +#### MIT License #### + +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original MIT license, with the additional +copyright staring in 2011 when the project was ported over: + + apic.go emitterc.go parserc.go readerc.go scannerc.go + writerc.go yamlh.go yamlprivateh.go + +Copyright (c) 2006-2010 Kirill Simonov +Copyright (c) 2006-2011 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +### Apache License ### + +All the remaining project files are covered by the Apache license: + +Copyright (c) 2011-2019 Canonical Ltd + +Licensed 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. diff --git a/dist/licenses/license-skywalking.apache.org-repo-goapi.txt b/dist/licenses/license-skywalking.apache.org-repo-goapi.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/dist/licenses/license-skywalking.apache.org-repo-goapi.txt @@ -0,0 +1,201 @@ + 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. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/go.mod b/go.mod index b3186d5..640c9fe 100644 --- a/go.mod +++ b/go.mod @@ -1,30 +1,29 @@ module github.com/apache/skywalking-mcp -go 1.24.3 +go 1.25.0 require ( - github.com/apache/skywalking-cli v0.0.0-20250604010708-77b4c49e89c9 - github.com/mark3labs/mcp-go v0.43.2 + github.com/apache/skywalking-cli v0.0.0-20251013114703-9a1beab08413 + github.com/machinebox/graphql v0.2.2 + github.com/mark3labs/mcp-go v0.45.0 github.com/sirupsen/logrus v1.9.4 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 - skywalking.apache.org/repo/goapi v0.0.0-20250520033135-e237d585745f + skywalking.apache.org/repo/goapi v0.0.0-20251021000546-17778a1a5d70 ) require ( github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/jsonschema v0.13.0 // indirect - github.com/machinebox/graphql v0.2.2 // indirect - github.com/mailru/easyjson v0.9.0 // indirect + github.com/mailru/easyjson v0.9.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/sagikazarmark/locafero v0.11.0 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect @@ -32,7 +31,8 @@ require ( github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.34.0 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 0a126a4..7b9232c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -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/apache/skywalking-cli v0.0.0-20251013114703-9a1beab08413 h1:x5zJpQCh+BHC/S/s3VXk461BFD9ks4trOduwDiuMiG8= +github.com/apache/skywalking-cli v0.0.0-20251013114703-9a1beab08413/go.mod h1:PuQJmkxOGXkv8ZHU+lGmfkQ9lX8eCqcYa1p/A9/oouA= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= @@ -11,8 +11,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk 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.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/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= @@ -27,10 +27,10 @@ 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/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= -github.com/mark3labs/mcp-go v0.43.2 h1:21PUSlWWiSbUPQwXIJ5WKlETixpFpq+WBpbMGDSVy/I= -github.com/mark3labs/mcp-go v0.43.2/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw= +github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= +github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/mark3labs/mcp-go v0.45.0 h1:s0S8qR/9fWaQ3pHxz7pm1uQ0DrswoSnRIxKIjbiQtkc= +github.com/mark3labs/mcp-go v0.45.0/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw= 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= @@ -42,12 +42,10 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN 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.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= -github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= @@ -69,14 +67,14 @@ github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zI github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= 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.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= +skywalking.apache.org/repo/goapi v0.0.0-20251021000546-17778a1a5d70 h1:2dXK4Jy9rR9Ie32takmVerncK4bvLl21D+wd5pKkCKQ= +skywalking.apache.org/repo/goapi v0.0.0-20251021000546-17778a1a5d70/go.mod h1:Vj9vINJYsTQASPsbQ1i81YgH8nFC/Xds4GjcXvmRYwM= diff --git a/internal/prompts/analysis.go b/internal/prompts/analysis.go new file mode 100644 index 0000000..4c045fd --- /dev/null +++ b/internal/prompts/analysis.go @@ -0,0 +1,250 @@ +// 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 prompts + +import ( + "context" + "fmt" + + "github.com/mark3labs/mcp-go/mcp" +) + +func performanceAnalysisHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + args := request.Params.Arguments + serviceName := args["service_name"] + start := args["start"] + end := args["end"] + + if start == "" { + start = defaultDuration + } + if end == "" { + end = defaultEnd + } + + // Use the dynamic tool instructions + toolInstructions := generateToolInstructions("performance_analysis") + + prompt := fmt.Sprintf(`Please analyze the performance of service '%s' for the time range start="%s", end="%s". + +%s + +**Analysis Required:** + +Use start="%[2]s", end="%[3]s" on every tool call below. + +**Response Time Analysis** +- Use execute_mqe_expression with expression="service_resp_time", start="%[2]s", end="%[3]s" +- Use execute_mqe_expression with expression="service_percentile{p='50,75,90,95,99'}", start="%[2]s", end="%[3]s" +- Identify trends and anomalies + +**Success Rate and SLA** +- Use execute_mqe_expression with expression="service_sla / 100", start="%[2]s", end="%[3]s" +- Use execute_mqe_expression with expression="service_apdex / 10000", start="%[2]s", end="%[3]s" +- Track SLA compliance over time + +**Traffic Analysis** +- Use execute_mqe_expression with expression="service_cpm", start="%[2]s", end="%[3]s" +- Identify traffic patterns and peak periods + +**Error Analysis** +- Use query_traces with trace_state="error", start="%[2]s", end="%[3]s" to find error traces +- Identify most common error types and affected endpoints + +**Performance Bottlenecks** +- Use execute_mqe_expression with expression="top_n(endpoint_resp_time, 5, DES)", start="%[2]s", end="%[3]s" +- Use execute_mqe_expression with expression="top_n(endpoint_cpm, 5, DES)", start="%[2]s", end="%[3]s" + +Please provide actionable insights and specific recommendations based on the data.`, serviceName, start, end, toolInstructions) + + return &mcp.GetPromptResult{ + Description: "Performance analysis using SkyWalking tools", + Messages: []mcp.PromptMessage{ + { + Role: mcp.RoleUser, + Content: mcp.TextContent{ + Type: "text", + Text: prompt, + }, + }, + }, + }, nil +} + +func mqeQueryBuilderHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + args := request.Params.Arguments + queryType := args["query_type"] + metrics := args["metrics"] + conditions := args["conditions"] + + // Use the dynamic tool instructions + toolInstructions := generateToolInstructions("mqe_query_building") + + prompt := fmt.Sprintf(`Help me build an MQE (Metrics Query Expression) for the following requirement: + +Query Type: %s +Metrics: %s +Additional Conditions: %s + +%s + +**MQE Building Process:** + +**Step-by-step approach:** +- Explain the MQE syntax for this use case +- Provide the complete MQE expression +- Show example usage with different parameters +- Explain what each part of the expression does +- Suggest variations for different scenarios + +If there are multiple ways to achieve this, please show alternatives with pros and cons.`, + queryType, metrics, conditions, toolInstructions) + + return &mcp.GetPromptResult{ + Description: "MQE query building assistance", + Messages: []mcp.PromptMessage{ + { + Role: mcp.RoleUser, + Content: mcp.TextContent{ + Type: "text", + Text: prompt, + }, + }, + }, + }, nil +} + +func compareServicesHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + args := request.Params.Arguments + services := args["services"] + metrics := args["metrics"] + start := args["start"] + end := args["end"] + + if metrics == "" { + metrics = allMetrics + } + if start == "" { + start = defaultDuration + } + if end == "" { + end = defaultEnd + } + + prompt := fmt.Sprintf(`Please compare the following services: %s + +Time Range: start="%s", end="%s" +Metrics to Compare: %s + +Use start="%[2]s", end="%[3]s" on every execute_mqe_expression call. + +Comparison should include: + +1. **Performance Comparison** + - Response time comparison (average and percentiles) + - Throughput (CPM) comparison + - Success rate (SLA) comparison + +2. **Resource Utilization** + - CPU and memory usage if available + - Connection pool usage + +3. **Error Patterns** + - Error rate comparison + - Types of errors by service + +4. **Dependency Impact** + - How each service affects others + - Cascade failure risks + +5. **Relative Performance** + - Which service is the bottleneck + - Performance ratios + - Efficiency metrics + +Please present the comparison in a clear, tabular format where possible, and highlight significant differences.`, + services, start, end, metrics) + + return &mcp.GetPromptResult{ + Description: "Service comparison analysis", + Messages: []mcp.PromptMessage{ + { + Role: mcp.RoleUser, + Content: mcp.TextContent{ + Type: "text", + Text: prompt, + }, + }, + }, + }, nil +} + +func topServicesHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + args := request.Params.Arguments + metricName := args["metric_name"] + topN := args["top_n"] + order := args["order"] + + if topN == "" { + topN = "10" + } + if order == "" { + order = "DES" + } + + prompt := fmt.Sprintf(`Find top services using execute_mqe_expression tool: + +**Tool Configuration:** +- execute_mqe_expression with expression: "top_n(%s, %s, %s)" + +**Analysis Focus:** + +**Service Ranking** +- Get top %s services by %s +- Compare values against baseline +- Identify outliers or anomalies + +**Performance Insights** +- For CPM metrics: Find busiest services +- For response time: Find slowest services +- For SLA: Find services with issues + +**Actionable Recommendations** +- Services needing immediate attention +- Capacity planning insights +- Performance optimization targets + +**Follow-up Analysis** +- Use query_traces for error investigation +- Use execute_mqe_expression for additional metric analysis + +Provide ranked results with specific recommendations.`, metricName, topN, order, topN, metricName) + + return &mcp.GetPromptResult{ + Description: "Top services analysis", + Messages: []mcp.PromptMessage{ + { + Role: mcp.RoleUser, + Content: mcp.TextContent{ + Type: "text", + Text: prompt, + }, + }, + }, + }, nil +} diff --git a/internal/prompts/constants.go b/internal/prompts/constants.go new file mode 100644 index 0000000..641879c --- /dev/null +++ b/internal/prompts/constants.go @@ -0,0 +1,25 @@ +// 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 prompts + +// Constants for common values +const ( + defaultDuration = "-1h" + defaultEnd = "now" + allMetrics = "all" +) diff --git a/internal/prompts/prompts.go b/internal/prompts/prompts.go deleted file mode 100644 index 11057b8..0000000 --- a/internal/prompts/prompts.go +++ /dev/null @@ -1,685 +0,0 @@ -// 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 prompts - -import ( - "context" - "fmt" - - "github.com/mark3labs/mcp-go/mcp" - "github.com/mark3labs/mcp-go/server" -) - -// Constants for common values -const ( - defaultDuration = "-1h" - allMetrics = "all" -) - -// Tool capability mapping for different analysis types -var toolCapabilities = map[string][]string{ - "performance_analysis": { - "query_single_metrics", - "query_top_n_metrics", - "execute_mqe_expression", - }, - "trace_investigation": { - "query_traces", - "get_trace_details", - "get_cold_trace_details", - }, - "log_analysis": { - "query_logs", - }, - "mqe_query_building": { - "execute_mqe_expression", - "list_mqe_metrics", - "get_mqe_metric_type", - }, - "service_comparison": { - "query_single_metrics", - "query_top_n_metrics", - "execute_mqe_expression", - }, - "metrics_exploration": { - "list_mqe_metrics", - "get_mqe_metric_type", - }, -} - -// AddSkyWalkingPrompts registers all SkyWalking-related prompts -func AddSkyWalkingPrompts(s *server.MCPServer) { - addCoreAnalysisPrompts(s) - addTraceAnalysisPrompts(s) - addUtilityPrompts(s) -} - -func addCoreAnalysisPrompts(s *server.MCPServer) { - // Performance Analysis Prompt - s.AddPrompt(mcp.Prompt{ - Name: "analyze-performance", - Description: "Analyze service performance using metrics tools", - Arguments: []mcp.PromptArgument{ - {Name: "service_name", Description: "The name of the service to analyze", Required: true}, - {Name: "duration", Description: "Time duration for analysis. Examples: -1h (past hour), -30m (past 30 minutes), " + - "-7d (past 7 days), 1h (next hour), 24h (next 24 hours)", Required: false}, - }, - }, performanceAnalysisHandler) - - // Service Comparison Prompt - s.AddPrompt(mcp.Prompt{ - Name: "compare-services", - Description: "Compare performance metrics between multiple services", - Arguments: []mcp.PromptArgument{ - {Name: "services", Description: "Comma-separated list of service names to compare", Required: true}, - {Name: "metrics", Description: "Metrics to compare (response_time, sla, cpm, all)", Required: false}, - {Name: "time_range", Description: "Time range for comparison. Examples: -1h (last hour), -2h (last 2 hours), -1d (last day)", Required: false}, - }, - }, compareServicesHandler) - - // Top N Metrics Analysis - s.AddPrompt(mcp.Prompt{ - Name: "top-services", - Description: "Find top N services by various metrics", - Arguments: []mcp.PromptArgument{ - {Name: "metric_name", Description: "Metric to rank by (service_cpm, service_resp_time, service_sla)", Required: true}, - {Name: "top_n", Description: "Number of top services to return (default: 10)", Required: false}, - {Name: "order", Description: "Order direction (ASC, DES)", Required: false}, - }, - }, topServicesHandler) -} - -func addTraceAnalysisPrompts(s *server.MCPServer) { - // Trace Investigation Prompt - s.AddPrompt(mcp.Prompt{ - Name: "investigate-traces", - Description: "Investigate traces for errors and performance issues", - Arguments: []mcp.PromptArgument{ - {Name: "service_id", Description: "The service to investigate", Required: false}, - {Name: "trace_state", Description: "Filter by trace state (success, error, all)", Required: false}, - {Name: "duration", Description: "Time range to search. Examples: -1h (last hour), -30m (last 30 minutes). Default: -1h", Required: false}, - }, - }, traceInvestigationHandler) - - // Trace Deep Dive - s.AddPrompt(mcp.Prompt{ - Name: "trace-deep-dive", - Description: "Deep dive analysis of a specific trace", - Arguments: []mcp.PromptArgument{ - {Name: "trace_id", Description: "The trace ID to analyze", Required: true}, - {Name: "view", Description: "Analysis view (full, summary, errors_only)", Required: false}, - {Name: "check_cold_storage", Description: "Check cold storage if not found (true/false)", Required: false}, - }, - }, traceDeepDiveHandler) - - // Log Analysis Prompt - s.AddPrompt(mcp.Prompt{ - Name: "analyze-logs", - Description: "Analyze service logs for errors and patterns", - Arguments: []mcp.PromptArgument{ - {Name: "service_id", Description: "Service to analyze logs", Required: false}, - {Name: "log_level", Description: "Log level to filter (ERROR, WARN, INFO)", Required: false}, - {Name: "duration", Description: "Time range to analyze. Examples: -1h (last hour), -6h (last 6 hours). Default: -1h", Required: false}, - }, - }, logAnalysisHandler) -} - -func addUtilityPrompts(s *server.MCPServer) { - // MQE Query Builder Prompt - s.AddPrompt(mcp.Prompt{ - Name: "build-mqe-query", - Description: "Help build MQE (Metrics Query Expression) for complex queries", - Arguments: []mcp.PromptArgument{ - {Name: "query_type", Description: "Type of query (performance, comparison, trend, alert)", Required: true}, - {Name: "metrics", Description: "Comma-separated list of metrics to query", Required: true}, - {Name: "conditions", Description: "Additional conditions or filters", Required: false}, - }, - }, mqeQueryBuilderHandler) - - // MQE Metrics Explorer - s.AddPrompt(mcp.Prompt{ - Name: "explore-metrics", - Description: "Explore available metrics and their types", - Arguments: []mcp.PromptArgument{ - {Name: "pattern", Description: "Regex pattern to filter metrics", Required: false}, - {Name: "show_examples", Description: "Show usage examples for each metric (true/false)", Required: false}, - }, - }, exploreMetricsHandler) -} - -// Analysis execution chains for different types of analysis -var analysisChains = map[string][]struct { - Tool string - Purpose string -}{ - "performance_analysis": { - {Tool: "query_single_metrics", Purpose: "Get basic metrics like CPM, SLA, response time"}, - {Tool: "execute_mqe_expression", Purpose: "Calculate derivatives like SLA percentage, percentiles"}, - {Tool: "query_top_n_metrics", Purpose: "Identify top endpoints by response time or traffic"}, - {Tool: "query_traces", Purpose: "Find error traces for deeper investigation"}, - }, - "trace_investigation": { - {Tool: "query_traces", Purpose: "Search for traces with specific filters"}, - {Tool: "get_trace_details", Purpose: "Analyze individual traces in detail"}, - {Tool: "get_cold_trace_details", Purpose: "Check historical traces if not found in hot storage"}, - }, - "log_analysis": { - {Tool: "query_logs", Purpose: "Search and analyze log entries with filters"}, - }, - "mqe_query_building": { - {Tool: "list_mqe_metrics", Purpose: "Discover available metrics"}, - {Tool: "get_mqe_metric_type", Purpose: "Understand metric types and usage"}, - {Tool: "execute_mqe_expression", Purpose: "Test and execute the built expression"}, - }, -} - -// Helper function to generate tool usage instructions -func generateToolInstructions(analysisType string) string { - tools := toolCapabilities[analysisType] - chain := analysisChains[analysisType] - - if len(tools) == 0 { - return "No specific tools defined for this analysis type." - } - - instructions := "**Available Tools:**\n" - for _, tool := range tools { - instructions += fmt.Sprintf("- %s\n", tool) - } - - if len(chain) > 0 { - instructions += "\n**Recommended Analysis Workflow:**\n" - for i, step := range chain { - instructions += fmt.Sprintf("%d. %s: %s\n", i+1, step.Tool, step.Purpose) - } - } - - return instructions -} - -// Handler implementations - -func performanceAnalysisHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { - args := request.Params.Arguments - serviceName := args["service_name"] - duration := args["duration"] - - if duration == "" { - duration = defaultDuration - } - - // Use the dynamic tool instructions - toolInstructions := generateToolInstructions("performance_analysis") - - prompt := fmt.Sprintf(`Please analyze the performance of service '%s' over the last %s. - -%s - -**Analysis Required:** - -**Response Time Analysis** -- Use query_single_metrics with metrics_name="service_resp_time" to get average response time -- Use execute_mqe_expression with expression="service_percentile{p='50,75,90,95,99'}" to get percentiles -- Identify trends and anomalies - -**Success Rate and SLA** -- Use execute_mqe_expression with expression="service_sla * 100" to get success rate percentage -- Use query_single_metrics with metrics_name="service_apdex" for user satisfaction score -- Track SLA compliance over time - -**Traffic Analysis** -- Use query_single_metrics with metrics_name="service_cpm" to get calls per minute -- Identify traffic patterns and peak periods - -**Error Analysis** -- Use query_traces with trace_state="error" to find error traces -- Identify most common error types and affected endpoints - -**Performance Bottlenecks** -- Use query_top_n_metrics with metrics_name="endpoint_resp_time" and order="DES" to find slowest endpoints -- Use query_top_n_metrics with metrics_name="endpoint_cpm" to find high-traffic endpoints - -Please provide actionable insights and specific recommendations based on the data.`, serviceName, duration, toolInstructions) - - return &mcp.GetPromptResult{ - Description: "Performance analysis using SkyWalking tools", - Messages: []mcp.PromptMessage{ - { - Role: mcp.RoleUser, - Content: mcp.TextContent{ - Type: "text", - Text: prompt, - }, - }, - }, - }, nil -} - -func mqeQueryBuilderHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { - args := request.Params.Arguments - queryType := args["query_type"] - metrics := args["metrics"] - conditions := args["conditions"] - - // Use the dynamic tool instructions - toolInstructions := generateToolInstructions("mqe_query_building") - - prompt := fmt.Sprintf(`Help me build an MQE (Metrics Query Expression) for the following requirement: - -Query Type: %s -Metrics: %s -Additional Conditions: %s - -%s - -**MQE Building Process:** - -**Step-by-step approach:** -- Explain the MQE syntax for this use case -- Provide the complete MQE expression -- Show example usage with different parameters -- Explain what each part of the expression does -- Suggest variations for different scenarios - -If there are multiple ways to achieve this, please show alternatives with pros and cons.`, - queryType, metrics, conditions, toolInstructions) - - return &mcp.GetPromptResult{ - Description: "MQE query building assistance", - Messages: []mcp.PromptMessage{ - { - Role: mcp.RoleUser, - Content: mcp.TextContent{ - Type: "text", - Text: prompt, - }, - }, - }, - }, nil -} - -func compareServicesHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { - args := request.Params.Arguments - services := args["services"] - metrics := args["metrics"] - timeRange := args["time_range"] - - if metrics == "" { - metrics = allMetrics - } - if timeRange == "" { - timeRange = defaultDuration - } - - prompt := fmt.Sprintf(`Please compare the following services: %s - -Time Range: %s -Metrics to Compare: %s - -Comparison should include: - -1. **Performance Comparison** - - Response time comparison (average and percentiles) - - Throughput (CPM) comparison - - Success rate (SLA) comparison - -2. **Resource Utilization** - - CPU and memory usage if available - - Connection pool usage - -3. **Error Patterns** - - Error rate comparison - - Types of errors by service - -4. **Dependency Impact** - - How each service affects others - - Cascade failure risks - -5. **Relative Performance** - - Which service is the bottleneck - - Performance ratios - - Efficiency metrics - -Please present the comparison in a clear, tabular format where possible, and highlight significant differences.`, - services, timeRange, metrics) - - return &mcp.GetPromptResult{ - Description: "Service comparison analysis", - Messages: []mcp.PromptMessage{ - { - Role: mcp.RoleUser, - Content: mcp.TextContent{ - Type: "text", - Text: prompt, - }, - }, - }, - }, nil -} - -func traceInvestigationHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { - args := request.Params.Arguments - serviceID := args["service_id"] - traceState := args["trace_state"] - duration := args["duration"] - - if duration == "" { - duration = defaultDuration - } - if traceState == "" { - traceState = "all" - } - - // Use the dynamic tool instructions - toolInstructions := generateToolInstructions("trace_investigation") - - prompt := fmt.Sprintf(`Investigate traces with filters: service_id="%s", trace_state="%s", duration="%s". - -%s - -**Analysis Steps:** - -**Find Problematic Traces** -- First use query_traces with view="summary" to get overview -- Look for patterns in error traces, slow traces, or anomalies -- Note trace IDs that need deeper investigation - -**Deep Dive on Specific Traces** -- Use get_trace_details with identified trace_id -- Start with view="summary" for quick insights -- Use view="full" for complete span analysis -- Use view="errors_only" if focusing on errors - -**Performance Analysis** -- Look for traces with high duration using min_trace_duration filter -- Identify bottlenecks in span timings -- Check for cascading delays - -**Error Pattern Analysis** -- Use query_traces with trace_state="error" -- Group errors by type and service -- Identify error propagation paths - -**Historical Investigation** -- If recent data shows no issues, use cold storage tools -- Use get_cold_trace_details for older trace data - -Provide specific findings and actionable recommendations.`, serviceID, traceState, duration, toolInstructions) - - return &mcp.GetPromptResult{ - Description: "Trace investigation using query tools", - Messages: []mcp.PromptMessage{ - { - Role: mcp.RoleUser, - Content: mcp.TextContent{ - Type: "text", - Text: prompt, - }, - }, - }, - }, nil -} - -func logAnalysisHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { - args := request.Params.Arguments - serviceID := args["service_id"] - logLevel := args["log_level"] - duration := args["duration"] - - if duration == "" { - duration = defaultDuration - } - if logLevel == "" { - logLevel = "ERROR" - } - - prompt := fmt.Sprintf(`Analyze service logs using the query_logs tool: - -**Tool Configuration:** -- query_logs with following parameters: - - service_id: "%s" (if specified) - - tags: [{"key": "level", "value": "%s"}] for log level filtering - - duration: "%s" for time range - - cold: true if historical data needed - -**Analysis Steps:** - -**Log Pattern Analysis** -- Use query_logs to get recent logs for the service -- Filter by log level (ERROR, WARN, INFO) -- Look for recurring error patterns -- Identify frequency of different log types - -**Error Investigation** -- Focus on ERROR level logs first -- Group similar error messages -- Check for correlation with trace IDs -- Look for timestamp patterns - -**Performance Correlation** -- Compare log timestamps with performance issues -- Look for resource exhaustion indicators -- Check for timeout or connection errors - -**Troubleshooting Workflow** -- Start with ERROR logs in the specified time range -- Use trace_id from logs to get detailed trace analysis -- Cross-reference with metrics for full picture - -Provide specific log analysis findings and recommendations.`, serviceID, logLevel, duration) - - return &mcp.GetPromptResult{ - Description: "Log analysis using query_logs tool", - Messages: []mcp.PromptMessage{ - { - Role: mcp.RoleUser, - Content: mcp.TextContent{ - Type: "text", - Text: prompt, - }, - }, - }, - }, nil -} - -func topServicesHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { - args := request.Params.Arguments - metricName := args["metric_name"] - topN := args["top_n"] - order := args["order"] - - if topN == "" { - topN = "10" - } - if order == "" { - order = "DES" - } - - prompt := fmt.Sprintf(`Find top services using query_top_n_metrics tool: - -**Tool Configuration:** -- query_top_n_metrics with parameters: - - metrics_name: "%s" - - top_n: %s - - order: "%s" (DES for highest, ASC for lowest) - - duration: "-1h" (or specify custom range) - -**Analysis Focus:** - -**Service Ranking** -- Get top %s services by %s -- Compare values against baseline -- Identify outliers or anomalies - -**Performance Insights** -- For CPM metrics: Find busiest services -- For response time: Find slowest services -- For SLA: Find services with issues - -**Actionable Recommendations** -- Services needing immediate attention -- Capacity planning insights -- Performance optimization targets - -**Follow-up Analysis** -- Use query_single_metrics for detailed service analysis -- Use query_traces for error investigation -- Use execute_mqe_expression for complex calculations - -Provide ranked results with specific recommendations.`, metricName, topN, order, topN, metricName) - - return &mcp.GetPromptResult{ - Description: "Top services analysis", - Messages: []mcp.PromptMessage{ - { - Role: mcp.RoleUser, - Content: mcp.TextContent{ - Type: "text", - Text: prompt, - }, - }, - }, - }, nil -} - -func traceDeepDiveHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { - args := request.Params.Arguments - traceID := args["trace_id"] - view := args["view"] - checkColdStorage := args["check_cold_storage"] - - if view == "" { - view = "summary" - } - - prompt := fmt.Sprintf(`Perform deep dive analysis of trace %s: - -**Primary Analysis:** -- get_trace_details with trace_id: "%s" and view: "%s" -- Start with summary view for quick insights -- Use full view for complete span analysis -- Use errors_only view if trace has errors - -**Cold Storage Check:** -- If trace not found in hot storage and check_cold_storage is "%s" -- Use get_cold_trace_details with same trace_id -- Check historical data for older traces - -**Analysis Depth:** - -**Trace Structure Analysis** -- Service call flow and dependencies -- Span duration breakdown -- Critical path identification -- Parallel vs sequential operations - -**Performance Investigation** -- Identify bottleneck spans -- Database query performance -- External API call latency -- Resource wait times - -**Error Analysis** (if applicable) -- Error location and propagation -- Root cause identification -- Impact assessment - -**Optimization Opportunities** -- Redundant operations -- Caching possibilities -- Parallel processing potential -- Database query optimization - -Provide detailed trace analysis with specific optimization recommendations.`, traceID, traceID, view, checkColdStorage) - - return &mcp.GetPromptResult{ - Description: "Deep dive trace analysis", - Messages: []mcp.PromptMessage{ - { - Role: mcp.RoleUser, - Content: mcp.TextContent{ - Type: "text", - Text: prompt, - }, - }, - }, - }, nil -} - -func exploreMetricsHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { - args := request.Params.Arguments - pattern := args["pattern"] - showExamples := args["show_examples"] - - if pattern == "" { - pattern = ".*" // match all metrics - } - - // Use the dynamic tool instructions - toolInstructions := generateToolInstructions("metrics_exploration") - - prompt := fmt.Sprintf(`Explore available metrics with pattern: "%s". - -%s - -**Exploration Workflow:** - -**Discover Metrics** -- Use list_mqe_metrics to get all available metrics -- Filter by pattern if specified -- Review metric names and types - -**Understand Metric Types** -- For each interesting metric, use get_mqe_metric_type -- REGULAR_VALUE: Direct arithmetic operations -- LABELED_VALUE: Requires label selectors -- SAMPLED_RECORD: Complex record-based metrics - -**Usage Examples** (if show_examples is "%s"): -- REGULAR_VALUE: service_cpm, service_sla * 100 -- LABELED_VALUE: service_percentile{p='50,75,90,95,99'} -- Complex: avg(service_cpm), top_n(service_resp_time, 10, des) - -**Metric Categories:** -- Service metrics: service_sla, service_cpm, service_resp_time -- Instance metrics: service_instance_* -- Endpoint metrics: endpoint_* -- Relation metrics: service_relation_* -- Infrastructure metrics: service_cpu, service_memory - -**Best Practices:** -- Check metric type before using in expressions -- Use appropriate label selectors for LABELED_VALUE -- Combine metrics for comprehensive analysis -- Use aggregation functions for trend analysis - -Provide a comprehensive guide to available metrics and their usage.`, pattern, toolInstructions, showExamples) - - return &mcp.GetPromptResult{ - Description: "Metrics exploration guide", - Messages: []mcp.PromptMessage{ - { - Role: mcp.RoleUser, - Content: mcp.TextContent{ - Type: "text", - Text: prompt, - }, - }, - }, - }, nil -} diff --git a/internal/prompts/registry.go b/internal/prompts/registry.go new file mode 100644 index 0000000..8c9cf50 --- /dev/null +++ b/internal/prompts/registry.go @@ -0,0 +1,149 @@ +// 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 prompts + +import ( + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" +) + +// AddSkyWalkingPrompts registers all SkyWalking-related prompts +func AddSkyWalkingPrompts(s *server.MCPServer) { + addCoreAnalysisPrompts(s) + addTraceAnalysisPrompts(s) + addUtilityPrompts(s) +} + +func addCoreAnalysisPrompts(s *server.MCPServer) { + // Performance Analysis Prompt + s.AddPrompt(mcp.Prompt{ + Name: "analyze-performance", + Description: "Analyze service performance using metrics tools", + Arguments: []mcp.PromptArgument{ + {Name: "service_name", Description: "The name of the service to analyze", Required: true}, + {Name: "start", Description: `Start of the analysis window. Examples: "-1h", "-30m", "2024-01-01 12:00:00". Default: -1h`, Required: false}, + {Name: "end", Description: `End of the analysis window. Examples: "now", "2024-01-01 13:00:00". Default: now`, Required: false}, + }, + }, performanceAnalysisHandler) + + // Service Comparison Prompt + s.AddPrompt(mcp.Prompt{ + Name: "compare-services", + Description: "Compare performance metrics between multiple services", + Arguments: []mcp.PromptArgument{ + {Name: "services", Description: "Comma-separated list of service names to compare", Required: true}, + {Name: "metrics", Description: "Metrics to compare (response_time, sla, cpm, all)", Required: false}, + {Name: "start", Description: `Start of the comparison window. Examples: "-1h", "-2h", "-1d". Default: -1h`, Required: false}, + {Name: "end", Description: `End of the comparison window. Examples: "now", "2024-01-01 13:00:00". Default: now`, Required: false}, + }, + }, compareServicesHandler) + + // Top N Metrics Analysis + s.AddPrompt(mcp.Prompt{ + Name: "top-services", + Description: "Find top N services by various metrics", + Arguments: []mcp.PromptArgument{ + {Name: "metric_name", Description: "Metric to rank by (service_cpm, service_resp_time, service_sla)", Required: true}, + {Name: "top_n", Description: "Number of top services to return (default: 10)", Required: false}, + {Name: "order", Description: "Order direction (ASC, DES)", Required: false}, + }, + }, topServicesHandler) +} + +func addTraceAnalysisPrompts(s *server.MCPServer) { + // Trace Investigation Prompt + s.AddPrompt(mcp.Prompt{ + Name: "investigate-traces", + Description: "Investigate traces for errors and performance issues", + Arguments: []mcp.PromptArgument{ + {Name: "service_id", Description: "The service to investigate", Required: false}, + {Name: "trace_state", Description: "Filter by trace state (success, error, all)", Required: false}, + {Name: "start", Description: `Start of the search window. Examples: "-1h" (last hour), "-30m" (last 30 minutes). Default: -1h`, Required: false}, + {Name: "end", Description: `End of the search window. Examples: "now", "2024-01-01 13:00:00". Default: now`, Required: false}, + }, + }, traceInvestigationHandler) + + // Trace Deep Dive + s.AddPrompt(mcp.Prompt{ + Name: "trace-deep-dive", + Description: "Deep dive analysis of a specific trace", + Arguments: []mcp.PromptArgument{ + {Name: "trace_id", Description: "The trace ID to analyze", Required: true}, + {Name: "view", Description: "Analysis view (full, summary, errors_only)", Required: false}, + }, + }, traceDeepDiveHandler) + + // Log Analysis Prompt + s.AddPrompt(mcp.Prompt{ + Name: "analyze-logs", + Description: "Analyze service logs for errors and patterns", + Arguments: []mcp.PromptArgument{ + {Name: "service_id", Description: "Service to analyze logs", Required: false}, + {Name: "log_level", Description: "Log level to filter (ERROR, WARN, INFO)", Required: false}, + {Name: "start", Description: `Start of the analysis window. Examples: "-1h" (last hour), "-6h" (last 6 hours). Default: -1h`, Required: false}, + {Name: "end", Description: `End of the analysis window. Examples: "now", "2024-01-01 13:00:00". Default: now`, Required: false}, + }, + }, logAnalysisHandler) +} + +func addUtilityPrompts(s *server.MCPServer) { + // Service Topology Explorer + s.AddPrompt(mcp.Prompt{ + Name: "explore-service-topology", + Description: "Explore the service topology of a layer: list services, instances, endpoints, and processes within a time range", + Arguments: []mcp.PromptArgument{ + {Name: "layer", Description: "The layer to explore (e.g. GENERAL, MESH, K8S). Use list_layers if unknown.", Required: true}, + {Name: "start", Description: `Start time for the query. Examples: "2024-01-01 12:00:00", "-1h" (1 hour ago).`, Required: true}, + {Name: "end", Description: `End time for the query. Examples: "2024-01-01 13:00:00", "now".` + + ` Defaults to current time if omitted.`, Required: false}, + }, + }, exploreServiceTopologyHandler) + + // Generate Duration Prompt + s.AddPrompt(mcp.Prompt{ + Name: "generate_duration", + Description: "Convert a natural-language time range into a {start, end} duration object" + + " for use with list_instances, list_endpoints, list_processes, and similar tools", + Arguments: []mcp.PromptArgument{ + {Name: "time_range", Description: `Natural-language description of the desired time range.` + + ` Examples: "last hour", "past 30 minutes", "yesterday 9am to 5pm", "2024-01-01 12:00 to 13:00"`, + Required: true}, + }, + }, generateDurationHandler) + + // MQE Query Builder Prompt + s.AddPrompt(mcp.Prompt{ + Name: "build-mqe-query", + Description: "Help build MQE (Metrics Query Expression) for complex queries", + Arguments: []mcp.PromptArgument{ + {Name: "query_type", Description: "Type of query (performance, comparison, trend, alert)", Required: true}, + {Name: "metrics", Description: "Comma-separated list of metrics to query", Required: true}, + {Name: "conditions", Description: "Additional conditions or filters", Required: false}, + }, + }, mqeQueryBuilderHandler) + + // MQE Metrics Explorer + s.AddPrompt(mcp.Prompt{ + Name: "explore-metrics", + Description: "Explore available metrics and their types", + Arguments: []mcp.PromptArgument{ + {Name: "pattern", Description: "Regex pattern to filter metrics", Required: false}, + {Name: "show_examples", Description: "Show usage examples for each metric (true/false)", Required: false}, + }, + }, exploreMetricsHandler) +} diff --git a/internal/prompts/tools.go b/internal/prompts/tools.go new file mode 100644 index 0000000..eb3df16 --- /dev/null +++ b/internal/prompts/tools.go @@ -0,0 +1,92 @@ +// 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 prompts + +import "fmt" + +// Tool capability mapping for different analysis types +var toolCapabilities = map[string][]string{ + "performance_analysis": { + "execute_mqe_expression", + "query_traces", + }, + "trace_investigation": { + "query_traces", + }, + "log_analysis": { + "query_logs", + }, + "mqe_query_building": { + "execute_mqe_expression", + "list_mqe_metrics", + "get_mqe_metric_type", + }, + "service_comparison": { + "execute_mqe_expression", + }, + "metrics_exploration": { + "list_mqe_metrics", + "get_mqe_metric_type", + }, +} + +// Analysis execution chains for different types of analysis +var analysisChains = map[string][]struct { + Tool string + Purpose string +}{ + "performance_analysis": { + {Tool: "execute_mqe_expression", Purpose: "Query metrics like CPM, SLA, response time, percentiles, and top entities"}, + {Tool: "query_traces", Purpose: "Find error traces for deeper investigation"}, + }, + "trace_investigation": { + {Tool: "query_traces", Purpose: "Search for traces with specific filters and analyze results"}, + }, + "log_analysis": { + {Tool: "query_logs", Purpose: "Search and analyze log entries with filters"}, + }, + "mqe_query_building": { + {Tool: "list_mqe_metrics", Purpose: "Discover available metrics"}, + {Tool: "get_mqe_metric_type", Purpose: "Understand metric types and usage"}, + {Tool: "execute_mqe_expression", Purpose: "Test and execute the built expression"}, + }, +} + +// Helper function to generate tool usage instructions +func generateToolInstructions(analysisType string) string { + tools := toolCapabilities[analysisType] + chain := analysisChains[analysisType] + + if len(tools) == 0 { + return "No specific tools defined for this analysis type." + } + + instructions := "**Available Tools:**\n" + for _, tool := range tools { + instructions += fmt.Sprintf("- %s\n", tool) + } + + if len(chain) > 0 { + instructions += "\n**Recommended Analysis Workflow:**\n" + for i, step := range chain { + instructions += fmt.Sprintf("%d. %s: %s\n", i+1, step.Tool, step.Purpose) + } + } + + return instructions +} diff --git a/internal/prompts/trace.go b/internal/prompts/trace.go new file mode 100644 index 0000000..90bbf32 --- /dev/null +++ b/internal/prompts/trace.go @@ -0,0 +1,210 @@ +// 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 prompts + +import ( + "context" + "fmt" + + "github.com/mark3labs/mcp-go/mcp" +) + +func traceInvestigationHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + args := request.Params.Arguments + serviceID := args["service_id"] + traceState := args["trace_state"] + start := args["start"] + end := args["end"] + + if start == "" { + start = defaultDuration + } + if end == "" { + end = defaultEnd + } + if traceState == "" { + traceState = "all" + } + + // Use the dynamic tool instructions + toolInstructions := generateToolInstructions("trace_investigation") + + prompt := fmt.Sprintf(`Investigate traces with filters: service_id="%s", trace_state="%s", start="%s", end="%s". + +%s + +**Analysis Steps:** + +**Find Problematic Traces** +- First use query_traces with start="%[3]s", end="%[4]s", view="summary" to get overview +- Look for patterns in error traces, slow traces, or anomalies +- Note trace IDs that need deeper investigation + +**Deep Dive on Specific Traces** +- Use query_traces with the identified trace_id +- Start with view="summary" for quick insights +- Use view="full" for complete span analysis +- Use view="errors_only" if focusing on errors + +**Performance Analysis** +- Look for traces with high duration using min_trace_duration filter +- Identify bottlenecks in span timings +- Check for cascading delays + +**Error Pattern Analysis** +- Use query_traces with trace_state="error" +- Group errors by type and service +- Identify error propagation paths + +Provide specific findings and actionable recommendations.`, serviceID, traceState, start, end, toolInstructions) + + return &mcp.GetPromptResult{ + Description: "Trace investigation using query tools", + Messages: []mcp.PromptMessage{ + { + Role: mcp.RoleUser, + Content: mcp.TextContent{ + Type: "text", + Text: prompt, + }, + }, + }, + }, nil +} + +func logAnalysisHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + args := request.Params.Arguments + serviceID := args["service_id"] + logLevel := args["log_level"] + start := args["start"] + end := args["end"] + + if start == "" { + start = defaultDuration + } + if end == "" { + end = defaultEnd + } + if logLevel == "" { + logLevel = "ERROR" + } + + prompt := fmt.Sprintf(`Analyze service logs using the query_logs tool: + +**Tool Configuration:** +- query_logs with following parameters: + - service_id: "%s" (if specified) + - tags: [{"key": "level", "value": "%s"}] for log level filtering + - start: "%s", end: "%s" for time range + - cold: true if historical data needed + +**Analysis Steps:** + +**Log Pattern Analysis** +- Use query_logs to get recent logs for the service +- Filter by log level (ERROR, WARN, INFO) +- Look for recurring error patterns +- Identify frequency of different log types + +**Error Investigation** +- Focus on ERROR level logs first +- Group similar error messages +- Check for correlation with trace IDs +- Look for timestamp patterns + +**Performance Correlation** +- Compare log timestamps with performance issues +- Look for resource exhaustion indicators +- Check for timeout or connection errors + +**Troubleshooting Workflow** +- Start with ERROR logs in the specified time range +- Use trace_id from logs to get detailed trace analysis +- Cross-reference with metrics for full picture + +Provide specific log analysis findings and recommendations.`, serviceID, logLevel, start, end) + + return &mcp.GetPromptResult{ + Description: "Log analysis using query_logs tool", + Messages: []mcp.PromptMessage{ + { + Role: mcp.RoleUser, + Content: mcp.TextContent{ + Type: "text", + Text: prompt, + }, + }, + }, + }, nil +} + +func traceDeepDiveHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + args := request.Params.Arguments + traceID := args["trace_id"] + view := args["view"] + + if view == "" { + view = "summary" + } + + prompt := fmt.Sprintf(`Perform deep dive analysis of trace %s: + +**Primary Analysis:** +- Use query_traces with trace_id: "%s" and view: "%s" +- Start with summary view for quick insights +- Use full view for complete span analysis +- Use errors_only view if trace has errors + +**Trace Structure Analysis** +- Service call flow and dependencies +- Span duration breakdown +- Critical path identification +- Parallel vs sequential operations + +**Performance Investigation** +- Identify bottleneck spans +- Database query performance +- External API call latency +- Resource wait times + +**Error Analysis** (if applicable) +- Error location and propagation +- Root cause identification +- Impact assessment + +**Optimization Opportunities** +- Redundant operations +- Caching possibilities +- Parallel processing potential +- Database query optimization + +Provide detailed trace analysis with specific optimization recommendations.`, traceID, traceID, view) + + return &mcp.GetPromptResult{ + Description: "Deep dive trace analysis", + Messages: []mcp.PromptMessage{ + { + Role: mcp.RoleUser, + Content: mcp.TextContent{ + Type: "text", + Text: prompt, + }, + }, + }, + }, nil +} diff --git a/internal/prompts/utility.go b/internal/prompts/utility.go new file mode 100644 index 0000000..a9e66a8 --- /dev/null +++ b/internal/prompts/utility.go @@ -0,0 +1,181 @@ +// 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 prompts + +import ( + "context" + "fmt" + + "github.com/mark3labs/mcp-go/mcp" +) + +func generateDurationHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + timeRange := request.Params.Arguments["time_range"] + + prompt := fmt.Sprintf(`Convert the following time range description into a duration object with "start" and "end" fields. + +Time range: "%s" + +Rules: +- "start" and "end" must be strings in one of these formats: + - Relative: "-30m" (30 minutes ago), "-1h" (1 hour ago), "-7d" (7 days ago) + - Absolute: "2024-01-01 12:00:00" (YYYY-MM-DD HH:MM:SS) +- If the end of the range is the current time, set "end" to "now" +- Relative values are always negative (e.g. "-1h", not "1h") + +Output only a JSON object, for example: +{"start": "-1h", "end": "now"} + +This duration can be passed directly to tools such as list_instances, list_endpoints, and list_processes.`, timeRange) + + return &mcp.GetPromptResult{ + Description: "Generate a {start, end} duration object from a natural-language time range", + Messages: []mcp.PromptMessage{ + { + Role: mcp.RoleUser, + Content: mcp.TextContent{Type: "text", Text: prompt}, + }, + }, + }, nil +} + +func exploreServiceTopologyHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + args := request.Params.Arguments + layer := args["layer"] + start := args["start"] + end := args["end"] + + if end == "" { + end = defaultEnd + } + + prompt := fmt.Sprintf(`Explore the service topology of layer "%s" within the time range from "%s" to "%s". + +**Workflow:** + +**Step 1 – Discover services** +- Use list_services with layer="%s" to get all services in this layer +- Note the id of each service for the next steps + +**Step 2 – List instances per service** +- For each service of interest, use list_instances with: + - service_id: + - start: "%s" + - end: "%s" +- Review instance names, languages, and attributes + +**Step 3 – List endpoints per service** +- Use list_endpoints with: + - service_id: + - start: "%s" + - end: "%s" +- Note endpoint ids for use in metrics or trace queries + +**Step 4 – List processes per instance** +- For each instance of interest, use list_processes with: + - instance_id: + - start: "%s" + - end: "%s" +- Review process names, detect types, and labels + +**Summary to provide:** +- Total number of services, instances, endpoints, and processes found +- Any notable attributes or labels worth highlighting +- Suggested follow-up queries (e.g. metrics, traces, logs) for specific services or instances`, + layer, start, end, + layer, + start, end, + start, end, + start, end) + + return &mcp.GetPromptResult{ + Description: "Service topology exploration", + Messages: []mcp.PromptMessage{ + { + Role: mcp.RoleUser, + Content: mcp.TextContent{ + Type: "text", + Text: prompt, + }, + }, + }, + }, nil +} + +func exploreMetricsHandler(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + args := request.Params.Arguments + pattern := args["pattern"] + showExamples := args["show_examples"] + + if pattern == "" { + pattern = ".*" // match all metrics + } + + // Use the dynamic tool instructions + toolInstructions := generateToolInstructions("metrics_exploration") + + prompt := fmt.Sprintf(`Explore available metrics with pattern: "%s". + +%s + +**Exploration Workflow:** + +**Discover Metrics** +- Use list_mqe_metrics to get all available metrics +- Filter by pattern if specified +- Review metric names and types + +**Understand Metric Types** +- For each interesting metric, use get_mqe_metric_type +- REGULAR_VALUE: Direct arithmetic operations +- LABELED_VALUE: Requires label selectors +- SAMPLED_RECORD: Complex record-based metrics + +**Usage Examples** (if show_examples is "%s"): +- REGULAR_VALUE: service_cpm, service_sla * 100 +- LABELED_VALUE: service_percentile{p='50,75,90,95,99'} +- Complex: avg(service_cpm), top_n(service_resp_time, 10, des) + +**Metric Categories:** +- Service metrics: service_sla, service_cpm, service_resp_time +- Instance metrics: service_instance_* +- Endpoint metrics: endpoint_* +- Relation metrics: service_relation_* +- Infrastructure metrics: service_cpu, service_memory + +**Best Practices:** +- Check metric type before using in expressions +- Use appropriate label selectors for LABELED_VALUE +- Combine metrics for comprehensive analysis +- Use aggregation functions for trend analysis + +Provide a comprehensive guide to available metrics and their usage.`, pattern, toolInstructions, showExamples) + + return &mcp.GetPromptResult{ + Description: "Metrics exploration guide", + Messages: []mcp.PromptMessage{ + { + Role: mcp.RoleUser, + Content: mcp.TextContent{ + Type: "text", + Text: prompt, + }, + }, + }, + }, nil +} diff --git a/internal/swmcp/server.go b/internal/swmcp/server.go index da406fa..21edfbc 100644 --- a/internal/swmcp/server.go +++ b/internal/swmcp/server.go @@ -25,6 +25,7 @@ import ( "github.com/mark3labs/mcp-go/server" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/apache/skywalking-cli/pkg/contextkey" @@ -34,29 +35,24 @@ import ( "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", +// newMCPServer creates a new MCP server with all tools, resources, and prompts registered. +func newMCPServer() *server.MCPServer { + s := server.NewMCPServer( + "skywalking-mcp", "0.1.0", server.WithResourceCapabilities(true, true), server.WithPromptCapabilities(true), - server.WithLogging()) - - // add tools and capabilities to the MCP server - tools.AddTraceTools(mcpServer) - tools.AddMetricsTools(mcpServer) - tools.AddLogTools(mcpServer) - tools.AddMQETools(mcpServer) - - // add MQE documentation resources - resources.AddMQEResources(mcpServer) - - // add prompts for guided interactions - prompts.AddSkyWalkingPrompts(mcpServer) - - return mcpServer + server.WithLogging(), + ) + tools.AddTraceTools(s) + tools.AddLogTools(s) + tools.AddMQETools(s) + tools.AddMetadataTools(s) + tools.AddEventTools(s) + tools.AddAlarmTools(s) + tools.AddTopologyTools(s) + resources.AddMQEResources(s) + prompts.AddSkyWalkingPrompts(s) + return s } func initLogger(logFilePath string) (*logrus.Logger, error) { @@ -85,51 +81,44 @@ func WithSkyWalkingURLAndInsecure(ctx context.Context, url string, insecure bool return ctx } -const ( - skywalkingURLEnvVar = "SW_URL" -) - -// urlAndInsecureFromEnv extracts URL and insecure flag purely from environment variables. -func urlAndInsecureFromEnv() (string, bool) { - urlStr := os.Getenv(skywalkingURLEnvVar) +// configuredSkyWalkingURL returns the configured SkyWalking OAP URL. +// The value is sourced from the CLI/config binding for `--sw-url`, +// falling back to the built-in default when unset. +func configuredSkyWalkingURL() string { + urlStr := viper.GetString("url") if urlStr == "" { urlStr = config.DefaultSWURL } - return tools.FinalizeURL(urlStr), false + return tools.FinalizeURL(urlStr) } -// urlAndInsecureFromHeaders extracts URL and insecure flag for a request. -// URL is sourced from Header > Environment > Default. -// Insecure flag is now hardcoded to false. -func urlAndInsecureFromHeaders(req *http.Request) (string, bool) { +// urlFromHeaders extracts URL for a request. +// URL is sourced from Header > configured value > Default. +func urlFromHeaders(req *http.Request) string { urlStr := req.Header.Get("SW-URL") if urlStr == "" { - urlStr = os.Getenv(skywalkingURLEnvVar) - if urlStr == "" { - urlStr = config.DefaultSWURL - } + return configuredSkyWalkingURL() } - return tools.FinalizeURL(urlStr), false + return tools.FinalizeURL(urlStr) } -// WithSkyWalkingContextFromEnv injects the SkyWalking URL and insecure -// settings from environment variables into the context. -var WithSkyWalkingContextFromEnv server.StdioContextFunc = func(ctx context.Context) context.Context { - urlStr, _ := urlAndInsecureFromEnv() - return WithSkyWalkingURLAndInsecure(ctx, urlStr, false) +// WithSkyWalkingContextFromConfig injects the SkyWalking URL and insecure +// settings from global configuration into the context. +var WithSkyWalkingContextFromConfig server.StdioContextFunc = func(ctx context.Context) context.Context { + return WithSkyWalkingURLAndInsecure(ctx, configuredSkyWalkingURL(), false) } // withSkyWalkingContextFromRequest is the shared logic for enriching context from an http.Request. func withSkyWalkingContextFromRequest(ctx context.Context, req *http.Request) context.Context { - urlStr, _ := urlAndInsecureFromHeaders(req) + urlStr := urlFromHeaders(req) return WithSkyWalkingURLAndInsecure(ctx, urlStr, false) } // EnhanceStdioContextFunc returns a StdioContextFunc that enriches the context -// with SkyWalking settings from the environment. +// with SkyWalking settings from the global configuration. func EnhanceStdioContextFunc() server.StdioContextFunc { - return WithSkyWalkingContextFromEnv + return WithSkyWalkingContextFromConfig } // EnhanceSSEContextFunc returns a SSEContextFunc that enriches the context diff --git a/internal/swmcp/sse.go b/internal/swmcp/sse.go index fd3ccd6..14365a9 100644 --- a/internal/swmcp/sse.go +++ b/internal/swmcp/sse.go @@ -72,7 +72,7 @@ func runSSEServer(ctx context.Context, cfg *config.SSEServerConfig) error { } sseServer := server.NewSSEServer( - newMcpServer(), + newMCPServer(), server.WithStaticBasePath(cfg.BasePath), server.WithSSEContextFunc(EnhanceSSEContextFunc()), ) diff --git a/internal/swmcp/stdio.go b/internal/swmcp/stdio.go index 059ba77..33a3529 100644 --- a/internal/swmcp/stdio.go +++ b/internal/swmcp/stdio.go @@ -44,7 +44,7 @@ func NewStdioServer() *cobra.Command { RunE: func(_ *cobra.Command, _ []string) error { url := viper.GetString("url") if url == "" { - return errors.New("SW_URL must be specified") + return errors.New("--sw-url must be specified") } stdioServerConfig := config.StdioServerConfig{ @@ -66,7 +66,7 @@ func runStdioServer(ctx context.Context, cfg *config.StdioServerConfig) error { ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) defer stop() - stdioServer := server.NewStdioServer(newMcpServer()) + stdioServer := server.NewStdioServer(newMCPServer()) logger, err := initLogger(cfg.LogFilePath) if err != nil { diff --git a/internal/swmcp/streamable.go b/internal/swmcp/streamable.go index f3da2e5..0500352 100644 --- a/internal/swmcp/streamable.go +++ b/internal/swmcp/streamable.go @@ -57,7 +57,7 @@ func NewStreamable() *cobra.Command { // runStreamableServer starts the Streamable server with the provided configuration. func runStreamableServer(cfg *config.StreamableServerConfig) error { httpServer := server.NewStreamableHTTPServer( - newMcpServer(), + newMCPServer(), server.WithStateLess(true), server.WithLogger(log.StandardLogger()), server.WithHTTPContextFunc(EnhanceHTTPContextFunc()), diff --git a/internal/tools/alarm.go b/internal/tools/alarm.go new file mode 100644 index 0000000..25417dd --- /dev/null +++ b/internal/tools/alarm.go @@ -0,0 +1,124 @@ +// 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" + api "skywalking.apache.org/repo/goapi/query" + + swalarm "github.com/apache/skywalking-cli/pkg/graphql/alarm" +) + +// AddAlarmTools registers alarm-related tools with the MCP server +func AddAlarmTools(s *server.MCPServer) { + AlarmQueryTool.Register(s) +} + +type AlarmTag struct { + Key string `json:"key"` + Value string `json:"value"` +} + +type AlarmQueryRequest struct { + Scope string `json:"scope,omitempty"` + Keyword string `json:"keyword,omitempty"` + Tags []AlarmTag `json:"tags,omitempty"` + Start string `json:"start,omitempty"` + End string `json:"end,omitempty"` + Step string `json:"step,omitempty"` + PageNum int `json:"page_num,omitempty"` + PageSize int `json:"page_size,omitempty"` +} + +func buildAlarmQueryCondition(req *AlarmQueryRequest, timeCtx TimeContext) *swalarm.ListAlarmCondition { + duration := BuildDurationWithContext(req.Start, req.End, req.Step, false, DefaultDuration, timeCtx) + + var tags []*api.AlarmTag + for _, t := range req.Tags { + v := t.Value + tags = append(tags, &api.AlarmTag{Key: t.Key, Value: &v}) + } + + cond := &swalarm.ListAlarmCondition{ + Duration: &duration, + Keyword: req.Keyword, + Tags: tags, + Paging: BuildPagination(req.PageNum, req.PageSize), + } + + if req.Scope != "" { + cond.Scope = api.Scope(req.Scope) + } + + return cond +} + +func queryAlarms(ctx context.Context, req *AlarmQueryRequest) (*mcp.CallToolResult, error) { + timeCtx := GetTimeContext(ctx) + cond := buildAlarmQueryCondition(req, timeCtx) + + alarms, err := swalarm.Alarms(ctx, cond) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to query alarms: %v", err)), nil + } + + jsonBytes, err := json.Marshal(alarms) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +var AlarmQueryTool = NewTool( + "query_alarms", + `Query alarms from SkyWalking OAP. Alarms are triggered when metrics breach configured thresholds. + +Examples: +- {"start": "-1h"}: All alarms in the last hour +- {"scope": "Service", "start": "-30m"}: Service-level alarms in the last 30 minutes +- {"keyword": "timeout", "start": "-1h"}: Alarms whose message contains "timeout" +- {"tags": [{"key": "level", "value": "critical"}], "start": "-1h"}: Alarms with a specific tag`, + queryAlarms, + mcp.WithString("scope", + mcp.Enum("All", "Service", "ServiceInstance", "Endpoint", "Process", + "ServiceRelation", "ServiceInstanceRelation", "EndpointRelation", "ProcessRelation"), + mcp.Description("Scope to filter alarms.")), + mcp.WithString("keyword", mcp.Description("Keyword to filter alarm messages.")), + mcp.WithArray("tags", + mcp.Description("Array of alarm tags to filter by, each with key and value."), + mcp.Items(map[string]any{ + "type": "object", + "properties": map[string]any{ + "key": map[string]any{"type": "string"}, + "value": map[string]any{"type": "string"}, + }, + "required": []string{"key", "value"}, + }), + ), + mcp.WithString("start", mcp.Description("Start time for the query.")), + mcp.WithString("end", mcp.Description("End time for the query. Default is now.")), + mcp.WithString("step", mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), + mcp.Description("Time step granularity. If not specified, uses adaptive step sizing.")), + mcp.WithNumber("page_num", mcp.Description("Page number, default 1.")), + mcp.WithNumber("page_size", mcp.Description("Page size, default 15.")), +) diff --git a/internal/tools/common.go b/internal/tools/common.go index 970a27d..fc89e0a 100644 --- a/internal/tools/common.go +++ b/internal/tools/common.go @@ -18,10 +18,13 @@ package tools import ( + "context" "fmt" + "strconv" "strings" "time" + "github.com/apache/skywalking-cli/pkg/graphql/metadata" api "skywalking.apache.org/repo/goapi/query" ) @@ -63,25 +66,88 @@ func FormatTimeByStep(t time.Time, step api.Step) string { } } +// TimeContext provides server-aware time data for duration calculations. +type TimeContext struct { + NowUTC time.Time + Location *time.Location +} + +// NewTimeContext builds a time context from server TimeInfo, falling back to local UTC. +func NewTimeContext(timeInfo *api.TimeInfo) TimeContext { + nowUTC := time.Now().UTC() + location := time.UTC + + if timeInfo != nil { + if timeInfo.CurrentTimestamp != nil { + nowUTC = time.UnixMilli(*timeInfo.CurrentTimestamp).UTC() + } + if timeInfo.Timezone != nil { + if loc, ok := parseTimezoneOffset(*timeInfo.Timezone); ok { + location = loc + } + } + } + + return TimeContext{ + NowUTC: nowUTC, + Location: location, + } +} + +// GetTimeContext fetches server time info and builds a time context. +func GetTimeContext(ctx context.Context) TimeContext { + info, err := metadata.ServerTimeInfo(ctx) + if err != nil { + return NewTimeContext(nil) + } + return NewTimeContext(&info) +} + +func parseTimezoneOffset(offset string) (*time.Location, bool) { + if len(offset) != 5 || (offset[0] != '+' && offset[0] != '-') { + return nil, false + } + + hours, err := strconv.Atoi(offset[1:3]) + if err != nil { + return nil, false + } + minutes, err := strconv.Atoi(offset[3:5]) + if err != nil { + return nil, false + } + + totalSeconds := hours*3600 + minutes*60 + if offset[0] == '-' { + totalSeconds = -totalSeconds + } + + return time.FixedZone(offset, totalSeconds), true +} + // ParseDuration converts duration string to api.Duration func ParseDuration(durationStr string, coldStage bool) api.Duration { - now := time.Now() + return ParseDurationWithContext(durationStr, coldStage, NewTimeContext(nil)) +} + +// ParseDurationWithContext converts duration string to api.Duration using server time context. +func ParseDurationWithContext(durationStr string, coldStage bool, timeCtx TimeContext) api.Duration { var startTime, endTime time.Time var step api.Step duration, err := time.ParseDuration(durationStr) if err == nil { if duration < 0 { - startTime = now.Add(duration) - endTime = now + startTime = timeCtx.NowUTC.Add(duration) + endTime = timeCtx.NowUTC } else { - startTime = now - endTime = now.Add(duration) + startTime = timeCtx.NowUTC + endTime = timeCtx.NowUTC.Add(duration) } // Use adaptive step based on time range step = determineAdaptiveStep(startTime, endTime) } else { - startTime, endTime, step = parseLegacyDuration(durationStr) + startTime, endTime, step = parseLegacyDuration(durationStr, timeCtx.NowUTC) } if !step.IsValid() { @@ -112,10 +178,15 @@ func BuildPagination(pageNum, pageSize int) *api.Pagination { // BuildDuration creates duration from parameters func BuildDuration(start, end, step string, cold bool, defaultDurationMinutes int) api.Duration { + return BuildDurationWithContext(start, end, step, cold, defaultDurationMinutes, NewTimeContext(nil)) +} + +// BuildDurationWithContext creates duration from parameters using server time context. +func BuildDurationWithContext(start, end, step string, cold bool, defaultDurationMinutes int, timeCtx TimeContext) api.Duration { if start != "" || end != "" { stepEnum := api.Step(step) // Parse and format start and end times - startTime, endTime := parseStartEndTimes(start, end) + startTime, endTime := parseStartEndTimes(start, end, timeCtx) // If step is not provided or invalid, determine it adaptively based on time range if step == "" || !stepEnum.IsValid() { @@ -134,7 +205,7 @@ func BuildDuration(start, end, step string, cold bool, defaultDurationMinutes in defaultDurationMinutes = DefaultDuration } defaultDurationStr := fmt.Sprintf("-%dm", defaultDurationMinutes) - return ParseDuration(defaultDurationStr, cold) + return ParseDurationWithContext(defaultDurationStr, cold, timeCtx) } // determineAdaptiveStep determines the adaptive step based on the time range @@ -142,18 +213,19 @@ func determineAdaptiveStep(startTime, endTime time.Time) api.Step { duration := endTime.Sub(startTime) if duration >= 7*24*time.Hour { return api.StepDay - } else if duration >= 24*time.Hour { + } + if duration >= 24*time.Hour { return api.StepHour - } else if duration >= time.Hour { + } + if duration >= time.Hour { return api.StepMinute } - return api.StepSecond + return api.StepMinute } // parseLegacyDuration parses legacy duration strings like "7d", "24h" -func parseLegacyDuration(durationStr string) (startTime, endTime time.Time, step api.Step) { - now := time.Now() +func parseLegacyDuration(durationStr string, now time.Time) (startTime, endTime time.Time, step api.Step) { if len(durationStr) > 1 && (durationStr[len(durationStr)-1] == 'd' || durationStr[len(durationStr)-1] == 'D') { var days int if _, parseErr := fmt.Sscanf(durationStr[:len(durationStr)-1], "%d", &days); parseErr == nil && days > 0 { @@ -187,7 +259,7 @@ func parseLegacyDuration(durationStr string) (startTime, endTime time.Time, step } // parseAbsoluteTime tries to parse absolute time in various formats -func parseAbsoluteTime(timeStr string) (time.Time, bool) { +func parseAbsoluteTime(timeStr string, location *time.Location) (time.Time, bool) { timeFormats := []string{ "2006-01-02 15:04:05", "2006-01-02 15:04", @@ -198,7 +270,7 @@ func parseAbsoluteTime(timeStr string) (time.Time, bool) { } for _, format := range timeFormats { - if parsed, err := time.Parse(format, timeStr); err == nil { + if parsed, err := time.ParseInLocation(format, timeStr, location); err == nil { return parsed, true } } @@ -207,8 +279,8 @@ func parseAbsoluteTime(timeStr string) (time.Time, bool) { } // parseTimeString parses a time string (start or end) -func parseTimeString(timeStr string, defaultTime time.Time) time.Time { - now := time.Now() +func parseTimeString(timeStr string, defaultTime time.Time, timeCtx TimeContext) time.Time { + now := timeCtx.NowUTC if timeStr == "" { return defaultTime @@ -224,20 +296,20 @@ func parseTimeString(timeStr string, defaultTime time.Time) time.Time { } // Try absolute time - if parsed, ok := parseAbsoluteTime(timeStr); ok { - return parsed + if parsed, ok := parseAbsoluteTime(timeStr, timeCtx.Location); ok { + return parsed.In(time.UTC) } return defaultTime } // parseStartEndTimes parses start and end time strings -func parseStartEndTimes(start, end string) (startTime, endTime time.Time) { - now := time.Now() +func parseStartEndTimes(start, end string, timeCtx TimeContext) (startTime, endTime time.Time) { + now := timeCtx.NowUTC defaultStart := now.Add(-30 * time.Minute) // Default to 30 minutes ago - startTime = parseTimeString(start, defaultStart) - endTime = parseTimeString(end, now) + startTime = parseTimeString(start, defaultStart, timeCtx) + endTime = parseTimeString(end, now, timeCtx) return startTime, endTime } diff --git a/internal/tools/event.go b/internal/tools/event.go new file mode 100644 index 0000000..fb89082 --- /dev/null +++ b/internal/tools/event.go @@ -0,0 +1,140 @@ +// 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" + api "skywalking.apache.org/repo/goapi/query" + + swevent "github.com/apache/skywalking-cli/pkg/graphql/event" +) + +// AddEventTools registers event-related tools with the MCP server +func AddEventTools(s *server.MCPServer) { + EventQueryTool.Register(s) +} + +const orderASC = "ASC" + +type EventQueryRequest struct { + UUID string `json:"uuid,omitempty"` + Service string `json:"service,omitempty"` + ServiceInstance string `json:"service_instance,omitempty"` + Endpoint string `json:"endpoint,omitempty"` + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Layer string `json:"layer,omitempty"` + Start string `json:"start,omitempty"` + End string `json:"end,omitempty"` + Step string `json:"step,omitempty"` + Order string `json:"order,omitempty"` + PageNum int `json:"page_num,omitempty"` + PageSize int `json:"page_size,omitempty"` +} + +func buildEventQueryCondition(req *EventQueryRequest, timeCtx TimeContext) *api.EventQueryCondition { + duration := BuildDurationWithContext(req.Start, req.End, req.Step, false, DefaultDuration, timeCtx) + + cond := &api.EventQueryCondition{ + Time: &duration, + Paging: BuildPagination(req.PageNum, req.PageSize), + } + + if req.UUID != "" { + cond.UUID = &req.UUID + } + if req.Service != "" || req.ServiceInstance != "" || req.Endpoint != "" { + src := &api.SourceInput{} + if req.Service != "" { + src.Service = &req.Service + } + if req.ServiceInstance != "" { + src.ServiceInstance = &req.ServiceInstance + } + if req.Endpoint != "" { + src.Endpoint = &req.Endpoint + } + cond.Source = src + } + if req.Name != "" { + cond.Name = &req.Name + } + if req.Type != "" { + t := api.EventType(req.Type) + cond.Type = &t + } + if req.Layer != "" { + cond.Layer = &req.Layer + } + + order := api.OrderDes + if req.Order == orderASC { + order = api.OrderAsc + } + cond.Order = &order + + return cond +} + +func queryEvents(ctx context.Context, req *EventQueryRequest) (*mcp.CallToolResult, error) { + timeCtx := GetTimeContext(ctx) + cond := buildEventQueryCondition(req, timeCtx) + + events, err := swevent.Events(ctx, cond) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to query events: %v", err)), nil + } + + jsonBytes, err := json.Marshal(events) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +var EventQueryTool = NewTool( + "query_events", + `Query events from SkyWalking OAP. Events record changes or incidents on a service, instance, or endpoint (e.g. deployments, restarts, scaling). + +Examples: +- {"service": "Your_ApplicationName", "start": "-1h"}: Recent events for a service +- {"type": "Error", "start": "-30m"}: Error events in the last 30 minutes +- {"service": "Your_ApplicationName", "type": "Normal"}: Normal events for a service`, + queryEvents, + mcp.WithString("uuid", mcp.Description("Filter by event UUID.")), + mcp.WithString("service", mcp.Description("Service name to filter events.")), + mcp.WithString("service_instance", mcp.Description("Service instance name to filter events.")), + mcp.WithString("endpoint", mcp.Description("Endpoint name to filter events.")), + mcp.WithString("name", mcp.Description("Event name to filter.")), + mcp.WithString("type", mcp.Enum("Normal", "Error"), + mcp.Description("Event type: Normal or Error.")), + mcp.WithString("layer", mcp.Description("Layer to filter events.")), + mcp.WithString("start", mcp.Description("Start time for the query.")), + mcp.WithString("end", mcp.Description("End time for the query. Default is now.")), + mcp.WithString("step", mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), + mcp.Description("Time step granularity. If not specified, uses adaptive step sizing.")), + mcp.WithString("order", mcp.Enum(orderASC, "DES"), + mcp.Description("Order events by time: ASC (oldest first) or DES (newest first, default).")), + mcp.WithNumber("page_num", mcp.Description("Page number, default 1.")), + mcp.WithNumber("page_size", mcp.Description("Page size, default 15.")), +) diff --git a/internal/tools/log.go b/internal/tools/log.go index 1d5e2a6..414b78e 100644 --- a/internal/tools/log.go +++ b/internal/tools/log.go @@ -44,6 +44,8 @@ type LogQueryRequest struct { ServiceInstanceID string `json:"service_instance_id,omitempty"` EndpointID string `json:"endpoint_id,omitempty"` TraceID string `json:"trace_id,omitempty"` + SegmentID string `json:"segment_id,omitempty"` + SpanID *int `json:"span_id,omitempty"` Tags []LogTag `json:"tags,omitempty"` Start string `json:"start,omitempty"` End string `json:"end,omitempty"` @@ -51,11 +53,12 @@ type LogQueryRequest struct { Cold bool `json:"cold,omitempty"` PageNum int `json:"page_num,omitempty"` PageSize int `json:"page_size,omitempty"` + QueryOrder string `json:"query_order,omitempty"` } // buildLogQueryCondition builds the log query condition from request parameters -func buildLogQueryCondition(req *LogQueryRequest) *api.LogQueryCondition { - duration := BuildDuration(req.Start, req.End, req.Step, req.Cold, DefaultDuration) +func buildLogQueryCondition(req *LogQueryRequest, timeCtx TimeContext) *api.LogQueryCondition { + duration := BuildDurationWithContext(req.Start, req.End, req.Step, req.Cold, DefaultDuration, timeCtx) var tags []*api.LogTag for _, t := range req.Tags { @@ -65,13 +68,37 @@ func buildLogQueryCondition(req *LogQueryRequest) *api.LogQueryCondition { paging := BuildPagination(req.PageNum, req.PageSize) + order := api.OrderDes + if req.QueryOrder == "ASC" { + order = api.OrderAsc + } + cond := &api.LogQueryCondition{ ServiceID: &req.ServiceID, ServiceInstanceID: &req.ServiceInstanceID, EndpointID: &req.EndpointID, - RelatedTrace: &api.TraceScopeCondition{TraceID: req.TraceID}, QueryDuration: &duration, Paging: paging, + QueryOrder: &order, + } + + if req.TraceID != "" || req.SegmentID != "" || req.SpanID != nil { + traceScope := &api.TraceScopeCondition{} + + if req.TraceID != "" { + traceScope.TraceID = req.TraceID + } + + if req.SegmentID != "" { + // Only set SegmentID when it is actually provided to avoid filtering by an empty segment. + traceScope.SegmentID = &req.SegmentID + } + + if req.SpanID != nil { + traceScope.SpanID = req.SpanID + } + + cond.RelatedTrace = traceScope } if len(tags) > 0 { @@ -82,7 +109,8 @@ func buildLogQueryCondition(req *LogQueryRequest) *api.LogQueryCondition { // queryLogs queries logs from SkyWalking OAP func queryLogs(ctx context.Context, req *LogQueryRequest) (*mcp.CallToolResult, error) { - cond := buildLogQueryCondition(req) + timeCtx := GetTimeContext(ctx) + cond := buildLogQueryCondition(req, timeCtx) logs, err := swlog.Logs(ctx, cond) if err != nil { @@ -96,29 +124,42 @@ func queryLogs(ctx context.Context, req *LogQueryRequest) (*mcp.CallToolResult, return mcp.NewToolResultText(string(jsonBytes)), nil } -var LogQueryTool = NewTool[LogQueryRequest, *mcp.CallToolResult]( +var LogQueryTool = NewTool( "query_logs", `Query logs from SkyWalking OAP with flexible filters. Workflow: 1. Use this tool to find logs matching specific criteria 2. Specify one or more query conditions to narrow down results -3. Use duration to limit the time range for the search +3. Use start/end to limit the time range for the search 4. Supports filtering by service, instance, endpoint, trace, tags, and time 5. Supports cold storage query and pagination Examples: - {"service_id": "Your_ApplicationName", "start": "2024-06-01 12:00:00", "end": "2024-06-01 13:00:00"}: Query logs for a service in a time range - {"trace_id": "abc123..."}: Query logs related to a specific trace +- {"trace_id": "abc123...", "segment_id": "seg456...", "span_id": 0}: Query logs for a specific span - {"tags": [{"key": "level", "value": "ERROR"}], "cold": true}: Query error logs from cold storage`, queryLogs, mcp.WithString("service_id", mcp.Description("Service ID to filter logs.")), mcp.WithString("service_instance_id", mcp.Description("Service instance ID to filter logs.")), mcp.WithString("endpoint_id", mcp.Description("Endpoint ID to filter logs.")), - mcp.WithString("trace_id", mcp.Description("Related trace ID.")), - mcp.WithArray("tags", mcp.Description("Array of log tags, each with key and value.")), + mcp.WithString("trace_id", mcp.Description("Related trace ID to filter logs by trace scope.")), + mcp.WithString("segment_id", mcp.Description("Related segment ID to narrow logs to a specific segment within a trace.")), + mcp.WithNumber("span_id", mcp.Description("Related span ID to narrow logs to a specific span within a segment.")), + mcp.WithArray("tags", + mcp.Description("Array of log tags, each with key and value."), + mcp.Items(map[string]any{ + "type": "object", + "properties": map[string]any{ + "key": map[string]any{"type": "string"}, + "value": map[string]any{"type": "string"}, + }, + "required": []string{"key", "value"}, + }), + ), mcp.WithString("start", mcp.Description("Start time for the query.")), - mcp.WithString("end", mcp.Description("End time for the query.")), + mcp.WithString("end", mcp.Description("End time for the query. Default is now.")), mcp.WithString("step", mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), mcp.Description("Time step granularity: SECOND, MINUTE, HOUR, DAY. "+ "If not specified, uses adaptive step sizing: "+ @@ -126,4 +167,6 @@ Examples: mcp.WithBoolean("cold", mcp.Description("Whether to query from cold-stage storage.")), mcp.WithNumber("page_num", mcp.Description("Page number, default 1.")), mcp.WithNumber("page_size", mcp.Description("Page size, default 15.")), + mcp.WithString("query_order", mcp.Enum("ASC", "DES"), + mcp.Description("Order logs by timestamp: ASC (oldest first) or DES (newest first, default).")), ) diff --git a/internal/tools/metadata.go b/internal/tools/metadata.go new file mode 100644 index 0000000..1122323 --- /dev/null +++ b/internal/tools/metadata.go @@ -0,0 +1,323 @@ +// 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" + + "github.com/apache/skywalking-cli/pkg/graphql/metadata" + api "skywalking.apache.org/repo/goapi/query" +) + +// AddMetadataTools registers metadata-related tools with the MCP server +func AddMetadataTools(s *server.MCPServer) { + ListLayersTool.Register(s) + ListServicesTool.Register(s) + ListInstancesTool.Register(s) + ListEndpointsTool.Register(s) + ListProcessesTool.Register(s) +} + +// ListLayersRequest defines the parameters for the list_layers tool (no parameters needed) +type ListLayersRequest struct{} + +// listLayers queries available layers from SkyWalking OAP +func listLayers(ctx context.Context, _ *ListLayersRequest) (*mcp.CallToolResult, error) { + layers, err := metadata.ListLayers(ctx) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to list layers: %v", err)), nil + } + + jsonBytes, err := json.Marshal(layers) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +// ListEndpointsRequest defines the parameters for the list_endpoints tool +type ListEndpointsRequest struct { + ServiceID string `json:"service_id"` + Keyword string `json:"keyword"` + Limit int `json:"limit"` + Start string `json:"start"` + End string `json:"end"` + Step string `json:"step"` + Cold bool `json:"cold"` +} + +// listEndpoints searches endpoints for a given service from SkyWalking OAP +func listEndpoints(ctx context.Context, req *ListEndpointsRequest) (*mcp.CallToolResult, error) { + limit := req.Limit + if limit <= 0 { + limit = 100 + } + timeCtx := GetTimeContext(ctx) + var durationPtr *api.Duration + if req.Start != "" || req.End != "" { + d := BuildDurationWithContext(req.Start, req.End, req.Step, req.Cold, DefaultDuration, timeCtx) + durationPtr = &d + } + endpoints, err := metadata.SearchEndpoints(ctx, req.ServiceID, req.Keyword, limit, durationPtr) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to list endpoints: %v", err)), nil + } + + jsonBytes, err := json.Marshal(endpoints) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +// ListEndpointsTool lists endpoints of a service in SkyWalking OAP +var ListEndpointsTool = NewTool( + "list_endpoints", + `List endpoints of a service registered in SkyWalking OAP. + +An endpoint represents an individual API path or operation exposed by a service. +Use list_services to obtain a service ID before calling this tool. + +The response includes each endpoint's id and name. +The id can be used as a filter in metrics queries that accept an endpoint scope. + +Workflow: +1. Call list_layers to find available layers +2. Call list_services with a layer to find the service and its ID +3. Call this tool with the service ID to list its endpoints + +Examples: +- {"service_id": "abc123"}: List all endpoints of a service +- {"service_id": "abc123", "keyword": "/api/user", "limit": 20}: Search endpoints by keyword`, + listEndpoints, + mcp.WithTitleAnnotation("List service endpoints"), + mcp.WithString("service_id", mcp.Required(), + mcp.Description("The service ID to list endpoints for. Use list_services to obtain a service ID."), + ), + mcp.WithString("keyword", + mcp.Description("Keyword to filter endpoints by name. Leave empty to list all endpoints."), + ), + mcp.WithNumber("limit", + mcp.Description("Maximum number of endpoints to return. Defaults to 100."), + ), + mcp.WithString("start", + mcp.Description(`Start time for the query. Examples: "2024-01-01 12:00:00", "-1h" (1 hour ago).`), + ), + mcp.WithString("end", + mcp.Description(`End time for the query. Examples: "2024-01-01 13:00:00", "now". Defaults to current time if omitted.`), + ), + mcp.WithString("step", + mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), + mcp.Description("Time step granularity. If not specified, uses adaptive sizing: SECOND (<1h), MINUTE (1h-24h), HOUR (1d-7d), DAY (>7d)."), + ), + mcp.WithBoolean("cold", + mcp.Description("Whether to query from cold-stage storage. Set to true for historical data queries."), + ), +) + +// ListProcessesRequest defines the parameters for the list_processes tool +type ListProcessesRequest struct { + InstanceID string `json:"instance_id"` + Start string `json:"start"` + End string `json:"end"` + Step string `json:"step"` + Cold bool `json:"cold"` +} + +// listProcesses queries processes for a given service instance from SkyWalking OAP +func listProcesses(ctx context.Context, req *ListProcessesRequest) (*mcp.CallToolResult, error) { + timeCtx := GetTimeContext(ctx) + duration := BuildDurationWithContext(req.Start, req.End, req.Step, req.Cold, DefaultDuration, timeCtx) + processes, err := metadata.Processes(ctx, req.InstanceID, duration) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to list processes: %v", err)), nil + } + + jsonBytes, err := json.Marshal(processes) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +// ListProcessesTool lists processes of a service instance in SkyWalking OAP +var ListProcessesTool = NewTool( + "list_processes", + `List processes of a service instance registered in SkyWalking OAP. + +A process represents an individual OS or language-level process running within a service instance. +Use list_instances to obtain an instance ID before calling this tool. + +The response includes each process's id, name, serviceId, serviceName, instanceId, instanceName, +agentId, detectType, labels, and attributes. + +Workflow: +1. Call list_layers to find available layers +2. Call list_services with a layer to find the service and its ID +3. Call list_instances with the service ID to find an instance and its ID +4. Call this tool with the instance ID and a time range to list its processes + +Examples: +- {"instance_id": "abc123", "start": "-1h"}: List processes active in the past hour +- {"instance_id": "abc123", "start": "2024-01-01 12:00", "end": "2024-01-01 13:00"}: List processes in a time range`, + listProcesses, + mcp.WithTitleAnnotation("List instance processes"), + mcp.WithString("instance_id", mcp.Required(), + mcp.Description("The instance ID to list processes for. Use list_instances to obtain an instance ID."), + ), + mcp.WithString("start", mcp.Required(), + mcp.Description(`Start time for the query. Examples: "2024-01-01 12:00:00", "-1h" (1 hour ago), "-30m" (30 minutes ago).`), + ), + mcp.WithString("end", + mcp.Description(`End time for the query. Examples: "2024-01-01 13:00:00", "now". Defaults to current time if omitted.`), + ), + mcp.WithString("step", + mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), + mcp.Description("Time step granularity. If not specified, uses adaptive sizing: SECOND (<1h), MINUTE (1h-24h), HOUR (1d-7d), DAY (>7d)."), + ), + mcp.WithBoolean("cold", + mcp.Description("Whether to query from cold-stage storage. Set to true for historical data queries."), + ), +) + +// ListInstancesRequest defines the parameters for the list_instances tool +type ListInstancesRequest struct { + ServiceID string `json:"service_id"` + Start string `json:"start"` + End string `json:"end"` + Step string `json:"step"` + Cold bool `json:"cold"` +} + +// listInstances queries service instances from SkyWalking OAP +func listInstances(ctx context.Context, req *ListInstancesRequest) (*mcp.CallToolResult, error) { + timeCtx := GetTimeContext(ctx) + duration := BuildDurationWithContext(req.Start, req.End, req.Step, req.Cold, DefaultDuration, timeCtx) + instances, err := metadata.Instances(ctx, req.ServiceID, duration) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to list instances: %v", err)), nil + } + + jsonBytes, err := json.Marshal(instances) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +// ListInstancesTool lists all instances of a service in SkyWalking OAP +var ListInstancesTool = NewTool( + "list_instances", + `List all instances of a service registered in SkyWalking OAP. + +A service instance represents an individual running process of a service (e.g. a pod or JVM process). +Use list_services to obtain a service ID before calling this tool. + +The response includes each instance's id, name, language, instanceUUID, and attributes. +The id can be used as a filter in metrics or log queries that accept a service_instance_id. + +Workflow: +1. Call list_layers to find available layers +2. Call list_services with a layer to find the service and its ID +3. Call this tool with the service ID and a time range to list its instances + +Examples: +- {"service_id": "abc123", "start": "2024-01-01 12:00", "end": "2024-01-01 13:00"}: List instances in a time range +- {"service_id": "abc123", "start": "-1h"}: List instances active in the past hour`, + listInstances, + mcp.WithTitleAnnotation("List service instances"), + mcp.WithString("service_id", mcp.Required(), + mcp.Description("The service ID to list instances for. Use list_services to obtain a service ID."), + ), + mcp.WithString("start", mcp.Required(), + mcp.Description(`Start time for the query. Examples: "2024-01-01 12:00:00", "-1h" (1 hour ago), "-30m" (30 minutes ago).`), + ), + mcp.WithString("end", + mcp.Description(`End time for the query. Examples: "2024-01-01 13:00:00", "now". Defaults to current time if omitted.`), + ), + mcp.WithString("step", + mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), + mcp.Description("Time step granularity. If not specified, uses adaptive sizing: SECOND (<1h), MINUTE (1h-24h), HOUR (1d-7d), DAY (>7d)."), + ), + mcp.WithBoolean("cold", + mcp.Description("Whether to query from cold-stage storage. Set to true for historical data queries."), + ), +) + +// listServices queries services for a given layer from SkyWalking OAP +func listServices(ctx context.Context, req *ListServicesRequest) (*mcp.CallToolResult, error) { + services, err := metadata.ListLayerService(ctx, req.Layer) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to list services: %v", err)), nil + } + + jsonBytes, err := json.Marshal(services) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +// ListServicesTool lists all services for a given layer in SkyWalking OAP +var ListServicesTool = NewTool( + "list_services", + `List all services registered in SkyWalking OAP under a specific layer. + +A service represents a logical grouping of monitored workloads. Each service belongs to one +or more layers (e.g. GENERAL, MESH, K8S). Use list_layers first to discover available layers. + +The response includes each service's id, name, group, shortName, layers, and normal flag. +The id can be used as a filter in other tools such as query_logs or query_traces. + +Workflow: +1. Call list_layers to discover available layers +2. Call this tool with the desired layer to get the services in that layer + +Examples: +- {"layer": "GENERAL"}: List all services in the GENERAL layer +- {"layer": "MESH"}: List all services in the service mesh layer`, + listServices, + mcp.WithTitleAnnotation("List services by layer"), + mcp.WithString("layer", mcp.Required(), + mcp.Description("The layer to list services for. Use list_layers to get available layer names."), + ), +) + +// ListLayersTool lists all available layers in SkyWalking OAP +var ListLayersTool = NewTool( + "list_layers", + `List all available layers registered in SkyWalking OAP. + +A layer represents a technology or deployment environment in SkyWalking's topology, +such as GENERAL, MESH, K8S, OS_LINUX, etc. Layers are used to categorize services +and filter topology views. + +Workflow: +1. Call this tool to discover which layers are available in the monitored environment +2. Use the returned layer names when querying services or metrics that require a layer filter + +Examples: +- {}: List all layers (no parameters required)`, + listLayers, + mcp.WithTitleAnnotation("List available layers"), +) diff --git a/internal/tools/metric.go b/internal/tools/metric.go deleted file mode 100644 index e5e725f..0000000 --- a/internal/tools/metric.go +++ /dev/null @@ -1,478 +0,0 @@ -// 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/base64" - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/mark3labs/mcp-go/mcp" - "github.com/mark3labs/mcp-go/server" - api "skywalking.apache.org/repo/goapi/query" - - "github.com/apache/skywalking-cli/pkg/graphql/metrics" -) - -// AddMetricsTools registers metrics-related tools with the MCP server -func AddMetricsTools(mcp *server.MCPServer) { - SingleMetricsTool.Register(mcp) - TopNMetricsTool.Register(mcp) -} - -// Error messages -const ( - ErrMissingMetricsName = "missing required parameter: metrics_name" - ErrInvalidTopN = "top_n must be a positive integer" - ErrFailedToQueryMetrics = "failed to query metrics: %v" -) - -// SingleMetricsRequest defines the parameters for the single metrics tool -type SingleMetricsRequest struct { - MetricsName string `json:"metrics_name"` - Scope string `json:"scope,omitempty"` - ServiceName string `json:"service_name,omitempty"` - ServiceInstanceName string `json:"service_instance_name,omitempty"` - EndpointName string `json:"endpoint_name,omitempty"` - ProcessName string `json:"process_name,omitempty"` - DestServiceName string `json:"dest_service_name,omitempty"` - DestServiceInstanceName string `json:"dest_service_instance_name,omitempty"` - DestEndpointName string `json:"dest_endpoint_name,omitempty"` - DestProcessName string `json:"dest_process_name,omitempty"` - Duration string `json:"duration,omitempty"` - Start string `json:"start,omitempty"` - End string `json:"end,omitempty"` - Step string `json:"step,omitempty"` - Cold bool `json:"cold,omitempty"` -} - -// TopNMetricsRequest defines the parameters for the top N metrics tool -// ParentService and Normal are used for service/entity identification, matching swctl behavior. -type TopNMetricsRequest struct { - MetricsName string `json:"metrics_name"` - TopN int `json:"top_n"` - Order string `json:"order,omitempty"` - Scope string `json:"scope,omitempty"` - ServiceID string `json:"service_id,omitempty"` - ServiceName string `json:"service_name,omitempty"` - ParentService string `json:"parent_service,omitempty"` - Normal bool `json:"normal,omitempty"` - Duration string `json:"duration,omitempty"` - Start string `json:"start,omitempty"` - End string `json:"end,omitempty"` - Step string `json:"step,omitempty"` - Cold bool `json:"cold,omitempty"` -} - -// MetricsValue represents the result of metrics query -type MetricsValue struct { - Value int `json:"value"` -} - -// ParseScopeInTop infers the scope for topN metrics based on metricsName -func ParseScopeInTop(metricsName string) api.Scope { - scope := api.ScopeService - if strings.HasPrefix(metricsName, "service_instance") { - scope = api.ScopeServiceInstance - } else if strings.HasPrefix(metricsName, "endpoint") { - scope = api.ScopeEndpoint - } - return scope -} - -// validateSingleMetricsRequest validates single metrics request parameters -func validateSingleMetricsRequest(req *SingleMetricsRequest) error { - if req.MetricsName == "" { - return errors.New(ErrMissingMetricsName) - } - return nil -} - -// validateTopNMetricsRequest validates top N metrics request parameters -func validateTopNMetricsRequest(req *TopNMetricsRequest) error { - if req.MetricsName == "" { - return errors.New(ErrMissingMetricsName) - } - // Set default top_n to 5 if not provided - if req.TopN == 0 { - req.TopN = 5 - } - if req.TopN <= 0 { - return errors.New(ErrInvalidTopN) - } - return nil -} - -// buildTopNCondition builds the top N condition from request parameters -func buildTopNCondition(req *TopNMetricsRequest) *api.TopNCondition { - parentService := "" - normal := false - // Parse service-id if present, otherwise use ServiceName if provided - if req.ServiceID != "" { - var err error - parentService, normal, err = ParseServiceID(req.ServiceID) - if err != nil { - parentService = "" - normal = false - } - } else if req.ServiceName != "" { - parentService = req.ServiceName - } - - condition := &api.TopNCondition{ - Name: req.MetricsName, - ParentService: &parentService, - Normal: &normal, - TopN: req.TopN, - Order: api.OrderDes, - } - if req.Order != "" { - order := api.Order(req.Order) - if order.IsValid() { - condition.Order = order - } - } - // Always set scope, using ParseScopeInTop if not provided - var scope api.Scope - if req.Scope != "" { - scope = api.Scope(req.Scope) - } else { - scope = ParseScopeInTop(req.MetricsName) - } - condition.Scope = &scope - - return condition -} - -// ParseServiceID decodes a service id into service name and normal flag -func ParseServiceID(id string) (name string, isNormal bool, err error) { - if id == "" { - return "", false, nil - } - parts := strings.Split(id, ".") - if len(parts) != 2 { - return "", false, fmt.Errorf("invalid service id, cannot be splitted into 2 parts. %v", id) - } - nameBytes, err := base64.StdEncoding.DecodeString(parts[0]) - if err != nil { - return "", false, err - } - name = string(nameBytes) - isNormal = parts[1] == "1" - return name, isNormal, nil -} - -// buildMetricsCondition builds the metrics condition from request parameters -func buildMetricsCondition(req *SingleMetricsRequest) *api.MetricsCondition { - condition := &api.MetricsCondition{ - Name: req.MetricsName, - } - - entity := &api.Entity{} - if req.Scope != "" { - scope := api.Scope(req.Scope) - entity.Scope = &scope - } - if req.ServiceName != "" { - entity.ServiceName = &req.ServiceName - } - if req.ServiceInstanceName != "" { - entity.ServiceInstanceName = &req.ServiceInstanceName - } - if req.EndpointName != "" { - entity.EndpointName = &req.EndpointName - } - if req.ProcessName != "" { - entity.ProcessName = &req.ProcessName - } - if req.DestServiceName != "" { - entity.DestServiceName = &req.DestServiceName - } - if req.DestServiceInstanceName != "" { - entity.DestServiceInstanceName = &req.DestServiceInstanceName - } - if req.DestEndpointName != "" { - entity.DestEndpointName = &req.DestEndpointName - } - if req.DestProcessName != "" { - entity.DestProcessName = &req.DestProcessName - } - condition.Entity = entity - return condition -} - -// querySingleMetrics queries single-value metrics -func querySingleMetrics(ctx context.Context, req *SingleMetricsRequest) (*mcp.CallToolResult, error) { - if err := validateSingleMetricsRequest(req); err != nil { - return mcp.NewToolResultError(err.Error()), nil - } - condition := buildMetricsCondition(req) - - var duration api.Duration - if req.Duration != "" { - duration = ParseDuration(req.Duration, req.Cold) - } else { - duration = BuildDuration(req.Start, req.End, req.Step, req.Cold, 0) - } - - value, err := metrics.IntValues(ctx, *condition, duration) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf(ErrFailedToQueryMetrics, err)), nil - } - result := MetricsValue{Value: value} - jsonBytes, err := json.Marshal(result) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil - } - return mcp.NewToolResultText(string(jsonBytes)), nil -} - -// queryTopNMetrics queries top N metrics -func queryTopNMetrics(ctx context.Context, req *TopNMetricsRequest) (*mcp.CallToolResult, error) { - if err := validateTopNMetricsRequest(req); err != nil { - return mcp.NewToolResultError(err.Error()), nil - } - condition := buildTopNCondition(req) - - // Set default duration if none provided - if req.Duration == "" && req.Start == "" && req.End == "" { - req.Duration = "30m" - } - - var duration api.Duration - if req.Duration != "" { - duration = ParseDuration(req.Duration, req.Cold) - } else { - duration = BuildDuration(req.Start, req.End, req.Step, req.Cold, 0) - } - - values, err := metrics.SortMetrics(ctx, *condition, duration) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf(ErrFailedToQueryMetrics, err)), nil - } - jsonBytes, err := json.Marshal(values) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil - } - return mcp.NewToolResultText(string(jsonBytes)), nil -} - -// SingleMetricsTool is a tool for querying single-value metrics -var SingleMetricsTool = NewTool[SingleMetricsRequest, *mcp.CallToolResult]( - "query_single_metrics", - `This tool queries single-value metrics defined in backend OAL from SkyWalking OAP. - -Workflow: -1. Use this tool when you need to get a single metric value for a specific entity -2. Specify the metrics name and entity details (service, endpoint, etc.) -3. Set the time range for the query -4. Get the metric value as a single integer result - -Metrics Examples: -- service_cpm: Calls per minute for a service -- endpoint_cpm: Calls per minute for an endpoint -- service_resp_time: Response time for a service -- service_apdex: Apdex score for a service -- service_sla: SLA percentage for a service - -Entity Scopes: -- Service: Service-level metrics -- ServiceInstance: Service instance-level metrics -- Endpoint: Endpoint-level metrics -- Process: Process-level metrics -- ServiceRelation: Service relationship metrics -- ServiceInstanceRelation: Service instance relationship metrics -- EndpointRelation: Endpoint relationship metrics -- ProcessRelation: Process relationship metrics - -Time Format: -- Absolute time: "2023-01-01 12:00:00", "2023-01-01 12" -- Relative time: "-30m" (30 minutes ago), "-1h" (1 hour ago) -- Step: "SECOND", "MINUTE", "HOUR", "DAY" - -Examples: -- {"metrics_name": "service_cpm", "service_name": "business-zone::projectC", "duration": "-1h"}: Get calls per minute for a service in the past hour -- {"metrics_name": "endpoint_cpm", "service_name": "business-zone::projectC", - "endpoint_name": "/projectC/{value}", "duration": "-30m"}: Get calls per minute for a specific endpoint in the past 30 minutes -- {"metrics_name": "service_resp_time", "service_name": "web-service", - "start": "-1h", "end": "now", "step": "MINUTE"}: Get service response time with custom time range -- {"metrics_name": "service_apdex", "service_name": "api-gateway", "cold": true}: Get Apdex score from cold storage`, - querySingleMetrics, - mcp.WithTitleAnnotation("Query single-value metrics"), - mcp.WithString("metrics_name", mcp.Required(), - mcp.Description(`The name of the metrics to query. Examples: service_sla, endpoint_sla, -service_instance_sla, service_cpm, service_resp_time, service_apdex`), - ), - mcp.WithString("scope", - mcp.Enum(string(api.ScopeAll), string(api.ScopeService), string(api.ScopeServiceInstance), string(api.ScopeEndpoint), string(api.ScopeProcess), - string(api.ScopeServiceRelation), string(api.ScopeServiceInstanceRelation), string(api.ScopeEndpointRelation), string(api.ScopeProcessRelation)), - mcp.Description(`The scope of the metrics entity: -- 'Service': Service-level metrics (default) -- 'ServiceInstance': Service instance-level metrics -- 'Endpoint': Endpoint-level metrics -- 'Process': Process-level metrics -- 'ServiceRelation': Service relationship metrics -- 'ServiceInstanceRelation': Service instance relationship metrics -- 'EndpointRelation': Endpoint relationship metrics -- 'ProcessRelation': Process relationship metrics`), - ), - mcp.WithString("service_name", - mcp.Description("Service name to filter metrics. Use this to get metrics for a specific service."), - ), - mcp.WithString("service_instance_name", - mcp.Description("Service instance name to filter metrics. Use this to get metrics for a specific service instance."), - ), - mcp.WithString("endpoint_name", - mcp.Description("Endpoint name to filter metrics. Use this to get metrics for a specific endpoint."), - ), - mcp.WithString("process_name", - mcp.Description("Process name to filter metrics. Use this to get metrics for a specific process."), - ), - mcp.WithString("dest_service_name", - mcp.Description("Destination service name for relationship metrics. Use this for service relation scopes."), - ), - mcp.WithString("dest_service_instance_name", - mcp.Description("Destination service instance name for relationship metrics. Use this for service instance relation scopes."), - ), - mcp.WithString("dest_endpoint_name", - mcp.Description("Destination endpoint name for relationship metrics. Use this for endpoint relation scopes."), - ), - mcp.WithString("dest_process_name", - mcp.Description("Destination process name for relationship metrics. Use this for process relation scopes."), - ), - mcp.WithString("duration", - mcp.Description("Time duration for the query relative to current time. "+ - "Negative values query the past: \"-1h\" (past 1 hour), \"-30m\" (past 30 minutes), \"-7d\" (past 7 days). "+ - "Positive values query the future: \"1h\" (next 1 hour), \"24h\" (next 24 hours)"), - ), - mcp.WithString("start", - mcp.Description("Start time for the query. Examples: \"2023-01-01 12:00:00\", \"-1h\" (1 hour ago), \"-30m\" (30 minutes ago)"), - ), - mcp.WithString("end", - mcp.Description("End time for the query. Examples: \"2023-01-01 13:00:00\", \"now\", \"-10m\" (10 minutes ago)"), - ), - mcp.WithString("step", - mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), - mcp.Description(`Time step between start time and end time: -|- 'SECOND': Second-level granularity -|- 'MINUTE': Minute-level granularity -|- 'HOUR': Hour-level granularity -|- 'DAY': Day-level granularity -If not specified, uses adaptive step sizing: -SECOND (<1h), MINUTE (1h-24h), HOUR (1d-7d), DAY (>7d)`), - ), - mcp.WithBoolean("cold", - mcp.Description("Whether to query from cold-stage storage. Set to true for historical data queries."), - ), -) - -// TopNMetricsTool is a tool for querying top N metrics -var TopNMetricsTool = NewTool[TopNMetricsRequest, *mcp.CallToolResult]( - "query_top_n_metrics", - `This tool queries the top N entities sorted by the specified metrics from SkyWalking OAP. - -Workflow: -1. Use this tool when you need to find the top N entities based on a specific metric -2. Specify the metrics name and the number of top entities to retrieve -3. Set the time range for the query -4. Get a list of top N entities with their metric values - -Metrics Examples: -- service_sla: SLA percentage for services -- endpoint_sla: SLA percentage for endpoints -- service_instance_sla: SLA percentage for service instances -- service_cpm: Calls per minute for services -- service_resp_time: Response time for services -- service_apdex: Apdex score for services - -Entity Scopes: -- Service: Service-level metrics (default) -- ServiceInstance: Service instance-level metrics -- Endpoint: Endpoint-level metrics -- Process: Process-level metrics - -Order Options: -- ASC: Ascending order (lowest values first) -- DES: Descending order (highest values first, default) - -Time Format: -- Absolute time: "2023-01-01 12:00:00", "2023-01-01 12" -- Relative time: "-30m" (30 minutes ago), "-1h" (1 hour ago) -- Step: "SECOND", "MINUTE", "HOUR", "DAY" - -Examples: -- {"metrics_name": "service_sla", "top_n": 5, "duration": "-1h"}: Get top 5 services with highest SLA in the past hour -- {"metrics_name": "endpoint_sla", "top_n": 10, "order": "ASC", "duration": "-30m"}: Get top 10 endpoints with lowest SLA in the past 30 minutes -- {"metrics_name": "service_instance_sla", "top_n": 3, "service_name": "boutique::adservice", - "duration": "-1h"}: Get top 3 instances of a specific service with highest SLA in the past hour -- {"metrics_name": "service_cpm", "top_n": 5, "start": "-1h", "end": "now", - "step": "MINUTE"}: Get top 5 services with highest calls per minute with custom time range`, - queryTopNMetrics, - mcp.WithTitleAnnotation("Query top N metrics"), - mcp.WithString("metrics_name", mcp.Required(), - mcp.Description(`The name of the metrics to query. Examples: service_sla, endpoint_sla, -service_instance_sla, service_cpm, service_resp_time, service_apdex`), - ), - mcp.WithNumber("top_n", mcp.Required(), - mcp.Description("The number of top entities to retrieve. Must be a positive integer."), - ), - mcp.WithString("order", - mcp.Enum("ASC", "DES"), - mcp.Description(`The order by which the top entities are sorted: -- 'ASC': Ascending order (lowest values first) -- 'DES': Descending order (highest values first, default)`), - ), - mcp.WithString("scope", - mcp.Enum(string(api.ScopeAll), string(api.ScopeService), string(api.ScopeServiceInstance), string(api.ScopeEndpoint), string(api.ScopeProcess)), - mcp.Description(`The scope of the metrics entity: -- 'Service': Service-level metrics (default) -- 'ServiceInstance': Service instance-level metrics -- 'Endpoint': Endpoint-level metrics -- 'Process': Process-level metrics`), - ), - mcp.WithString("service_name", - mcp.Description("Parent service name to filter metrics. Use this to get top N entities within a specific service."), - ), - mcp.WithString("duration", - mcp.Description("Time duration for the query relative to current time. "+ - "Negative values query the past: \"-1h\" (past 1 hour), \"-30m\" (past 30 minutes), \"-7d\" (past 7 days). "+ - "Positive values query the future: \"1h\" (next 1 hour), \"24h\" (next 24 hours)"), - ), - mcp.WithString("start", - mcp.Description("Start time for the query. Examples: \"2023-01-01 12:00:00\", \"-1h\" (1 hour ago), \"-30m\" (30 minutes ago)"), - ), - mcp.WithString("end", - mcp.Description("End time for the query. Examples: \"2023-01-01 13:00:00\", \"now\", \"-10m\" (10 minutes ago)"), - ), - mcp.WithString("step", - mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), - mcp.Description(`Time step between start time and end time: -|- 'SECOND': Second-level granularity -|- 'MINUTE': Minute-level granularity -|- 'HOUR': Hour-level granularity -|- 'DAY': Day-level granularity -If not specified, uses adaptive step sizing: -SECOND (<1h), MINUTE (1h-24h), HOUR (1d-7d), DAY (>7d)`), - ), - mcp.WithBoolean("cold", - mcp.Description("Whether to query from cold-stage storage. Set to true for historical data queries."), - ), -) diff --git a/internal/tools/mqe.go b/internal/tools/mqe.go index 68afd31..f152d49 100644 --- a/internal/tools/mqe.go +++ b/internal/tools/mqe.go @@ -30,7 +30,6 @@ import ( "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" "github.com/spf13/viper" - api "skywalking.apache.org/repo/goapi/query" ) // AddMQETools registers MQE-related tools with the MCP server @@ -118,7 +117,6 @@ type MQEExpressionRequest struct { DestEndpointName string `json:"dest_endpoint_name,omitempty"` DestProcessName string `json:"dest_process_name,omitempty"` DestNormal *bool `json:"dest_normal,omitempty"` - Duration string `json:"duration,omitempty"` Start string `json:"start,omitempty"` End string `json:"end,omitempty"` Step string `json:"step,omitempty"` @@ -292,13 +290,9 @@ func executeMQEExpression(ctx context.Context, req *MQEExpressionRequest) (*mcp. } entity := buildMQEEntity(ctx, req) + timeCtx := GetTimeContext(ctx) - var duration api.Duration - if req.Duration != "" { - duration = ParseDuration(req.Duration, req.Cold) - } else { - duration = BuildDuration(req.Start, req.End, req.Step, req.Cold, DefaultDuration) - } + duration := BuildDurationWithContext(req.Start, req.End, req.Step, req.Cold, DefaultDuration, timeCtx) // GraphQL query for MQE expression query := ` @@ -449,7 +443,7 @@ func getMQEMetricsType(ctx context.Context, req *MQEMetricsTypeRequest) (*mcp.Ca return mcp.NewToolResultText(string(jsonBytes)), nil } -var MQEExpressionTool = NewTool[MQEExpressionRequest, *mcp.CallToolResult]( +var MQEExpressionTool = NewTool( "execute_mqe_expression", `Execute MQE (Metrics Query Expression) to query and calculate metrics data. @@ -478,7 +472,7 @@ USAGE REQUIREMENTS: - The 'expression' parameter is mandatory for all queries - For service-specific queries, specify 'service_name' and optionally 'layer' (defaults to GENERAL) - For relation metrics, provide both source and destination entity parameters -- Either specify 'duration' OR both 'start' and 'end' for time range +- Use 'start' and 'end' to set a time range; if omitted, defaults to the last 30 minutes - Use 'debug: true' for query tracing and troubleshooting - Use 'cold: true' to query from cold storage (BanyanDB only) @@ -490,13 +484,14 @@ Entity Filtering (all optional): - Relation queries: dest_service_name + dest_layer, dest_service_instance_name, etc. Examples: -- {expression: "service_sla * 100", service_name: "Your_ApplicationName", layer: "GENERAL", duration: "-1h"}: Convert SLA to percentage for last hour +- {expression: "service_sla * 100", service_name: "Your_ApplicationName", layer: "GENERAL", + start: "-1h", end: "now"}: Convert SLA to percentage for last hour - {expression: "service_resp_time > 3000 && service_cpm < 1000", service_name: "Your_ApplicationName", - duration: "-30m"}: Find high latency with low traffic in last 30 minutes -- {expression: "avg(service_cpm)", duration: "-2h"}: Calculate average CPM for last 2 hours -- {expression: "service_cpm", duration: "24h"}: Query CPM for next 24 hours (useful for capacity planning) + start: "-30m", end: "now"}: Find high latency with low traffic in last 30 minutes +- {expression: "avg(service_cpm)", start: "-2h", end: "now"}: Calculate average CPM for last 2 hours +- {expression: "service_cpm", start: "now", end: "+24h"}: Query CPM for next 24 hours (useful for capacity planning) - {expression: "top_n(service_cpm, 10, des)", start: "2025-07-06 16:00:00", end: "2025-07-06 17:00:00", - step: "MINUTE"}: Top 10 services by CPM with minute granularity`, + step: "MINUTE"}: Top 10 services by CPM with minute granularity`, executeMQEExpression, mcp.WithString("expression", mcp.Required(), mcp.Description("MQE expression to execute (required). "+ @@ -520,11 +515,6 @@ Examples: mcp.WithString("dest_endpoint_name", mcp.Description("Destination endpoint name for relation metrics")), mcp.WithString("dest_process_name", mcp.Description("Destination process name for relation metrics")), mcp.WithBoolean("dest_normal", mcp.Description("Whether the destination service is normal")), - mcp.WithString("duration", - mcp.Description("Time duration for the query relative to current time. "+ - "Negative values query the past: `-1h` (past 1 hour), `-30m` (past 30 minutes), `-7d` (past 7 days). "+ - "Positive values query the future: `1h` (next 1 hour), `24h` (next 24 hours). "+ - "Use this OR specify both start+end")), mcp.WithString("start", mcp.Description("Start time for the query. Examples: `2025-07-06 12:00:00`, `-1h` (1 hour ago), `-30m` (30 minutes ago)")), mcp.WithString("end", mcp.Description("End time for the query. Examples: `2025-07-06 13:00:00`, `now`, `-10m` (10 minutes ago)")), mcp.WithString("step", mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY", "MONTH"), @@ -538,7 +528,7 @@ Examples: mcp.WithBoolean("dump_db_rsp", mcp.Description("Dump database response for debugging")), ) -var MQEMetricsListTool = NewTool[MQEMetricsListRequest, *mcp.CallToolResult]( +var MQEMetricsListTool = NewTool( "list_mqe_metrics", `List available metrics in SkyWalking that can be used in MQE expressions. @@ -573,7 +563,7 @@ Examples: mcp.WithString("regex", mcp.Description("Optional regex pattern to filter metrics by name. Examples: `service_.*`, `.*_cpm`, `endpoint_.*`")), ) -var MQEMetricsTypeTool = NewTool[MQEMetricsTypeRequest, *mcp.CallToolResult]( +var MQEMetricsTypeTool = NewTool( "get_mqe_metric_type", `Get type information for a specific metric. diff --git a/internal/tools/tools.go b/internal/tools/tools.go index 0286e3f..37e9b7d 100644 --- a/internal/tools/tools.go +++ b/internal/tools/tools.go @@ -48,7 +48,7 @@ func NewTool[T any, R any]( // 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...) + tool, handler, err := ConvertTool(t.Name, t.Description, t.Handler, t.Options...) if err != nil { panic(err) } diff --git a/internal/tools/topology.go b/internal/tools/topology.go new file mode 100644 index 0000000..f64a1f0 --- /dev/null +++ b/internal/tools/topology.go @@ -0,0 +1,247 @@ +// 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/machinebox/graphql" + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + api "skywalking.apache.org/repo/goapi/query" + + "github.com/apache/skywalking-cli/pkg/graphql/client" + "github.com/apache/skywalking-cli/pkg/graphql/dependency" +) + +// AddTopologyTools registers topology-related tools with the MCP server +func AddTopologyTools(s *server.MCPServer) { + ServicesTopologyTool.Register(s) + InstancesTopologyTool.Register(s) + EndpointsTopologyTool.Register(s) + ProcessesTopologyTool.Register(s) +} + +const getServicesTopologyGQL = ` +query ($serviceIds: [ID!]!, $duration: Duration!) { + result: getServicesTopology(serviceIds: $serviceIds, duration: $duration) { + nodes { id name type isReal layers } + calls { id source detectPoints target sourceComponents targetComponents } + } +}` + +func servicesTopology(ctx context.Context, serviceIDs []string, duration api.Duration) (api.Topology, error) { + var response map[string]api.Topology + request := graphql.NewRequest(getServicesTopologyGQL) + request.Var("serviceIds", serviceIDs) + request.Var("duration", duration) + err := client.ExecuteQuery(ctx, request, &response) + return response["result"], err +} + +type ServicesTopologyRequest struct { + ServiceIDs []string `json:"service_ids,omitempty"` + Layer string `json:"layer,omitempty"` + Start string `json:"start,omitempty"` + End string `json:"end,omitempty"` + Step string `json:"step,omitempty"` +} + +func queryServicesTopology(ctx context.Context, req *ServicesTopologyRequest) (*mcp.CallToolResult, error) { + timeCtx := GetTimeContext(ctx) + duration := BuildDurationWithContext(req.Start, req.End, req.Step, false, DefaultDuration, timeCtx) + + var ( + topology api.Topology + err error + ) + + if len(req.ServiceIDs) > 0 { + topology, err = servicesTopology(ctx, req.ServiceIDs, duration) + } else if req.Layer != "" { + topology, err = dependency.GlobalTopology(ctx, req.Layer, duration) + } else { + topology, err = dependency.GlobalTopologyWithoutLayer(ctx, duration) + } + + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to query topology: %v", err)), nil + } + + jsonBytes, err := json.Marshal(topology) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +type InstancesTopologyRequest struct { + ClientServiceID string `json:"client_service_id"` + ServerServiceID string `json:"server_service_id"` + Start string `json:"start,omitempty"` + End string `json:"end,omitempty"` + Step string `json:"step,omitempty"` +} + +func queryInstancesTopology(ctx context.Context, req *InstancesTopologyRequest) (*mcp.CallToolResult, error) { + timeCtx := GetTimeContext(ctx) + duration := BuildDurationWithContext(req.Start, req.End, req.Step, false, DefaultDuration, timeCtx) + + topology, err := dependency.InstanceTopology(ctx, req.ClientServiceID, req.ServerServiceID, duration) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to query instances topology: %v", err)), nil + } + + jsonBytes, err := json.Marshal(topology) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +type EndpointsTopologyRequest struct { + EndpointID string `json:"endpoint_id"` + Start string `json:"start,omitempty"` + End string `json:"end,omitempty"` + Step string `json:"step,omitempty"` +} + +func queryEndpointsTopology(ctx context.Context, req *EndpointsTopologyRequest) (*mcp.CallToolResult, error) { + timeCtx := GetTimeContext(ctx) + duration := BuildDurationWithContext(req.Start, req.End, req.Step, false, DefaultDuration, timeCtx) + + topology, err := dependency.EndpointDependency(ctx, req.EndpointID, duration) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to query endpoints topology: %v", err)), nil + } + + jsonBytes, err := json.Marshal(topology) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +type ProcessesTopologyRequest struct { + ServiceInstanceID string `json:"service_instance_id"` + Start string `json:"start,omitempty"` + End string `json:"end,omitempty"` + Step string `json:"step,omitempty"` +} + +func queryProcessesTopology(ctx context.Context, req *ProcessesTopologyRequest) (*mcp.CallToolResult, error) { + timeCtx := GetTimeContext(ctx) + duration := BuildDurationWithContext(req.Start, req.End, req.Step, false, DefaultDuration, timeCtx) + + topology, err := dependency.ProcessTopology(ctx, req.ServiceInstanceID, duration) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to query processes topology: %v", err)), nil + } + + jsonBytes, err := json.Marshal(topology) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil + } + return mcp.NewToolResultText(string(jsonBytes)), nil +} + +var ProcessesTopologyTool = NewTool( + "query_processes_topology", + `Query the process topology for a given service instance from SkyWalking OAP (getProcessTopology). + +Returns the topology of processes running within the given service instance, including process nodes and the calls between them. + +Examples: +- {"service_instance_id": "instance-id-1"}: Process topology for the last 30 minutes +- {"service_instance_id": "instance-id-1", "start": "-1h"}: Process topology for the last hour`, + queryProcessesTopology, + mcp.WithString("service_instance_id", + mcp.Required(), + mcp.Description("The ID of the service instance to query process topology for.")), + mcp.WithString("start", mcp.Description("Start time for the query.")), + mcp.WithString("end", mcp.Description("End time for the query. Default is now.")), + mcp.WithString("step", mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), + mcp.Description("Time step granularity. If not specified, uses adaptive step sizing.")), +) + +var EndpointsTopologyTool = NewTool( + "query_endpoints_topology", + `Query the endpoint dependency topology for a given endpoint from SkyWalking OAP (getEndpointDependencies). + +Returns the topology of endpoints that the given endpoint depends on or is depended upon by, including endpoint nodes and the calls between them. + +Examples: +- {"endpoint_id": "ep-id-1"}: Endpoint topology for the last 30 minutes +- {"endpoint_id": "ep-id-1", "start": "-1h"}: Endpoint topology for the last hour`, + queryEndpointsTopology, + mcp.WithString("endpoint_id", + mcp.Required(), + mcp.Description("The ID of the endpoint to query dependencies for.")), + mcp.WithString("start", mcp.Description("Start time for the query.")), + mcp.WithString("end", mcp.Description("End time for the query. Default is now.")), + mcp.WithString("step", mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), + mcp.Description("Time step granularity. If not specified, uses adaptive step sizing.")), +) + +var InstancesTopologyTool = NewTool( + "query_instances_topology", + `Query the service instance topology between two services from SkyWalking OAP (getServiceInstanceTopology). + +Returns the topology of service instances for the given client and server services, including instance nodes and the calls between them. + +Examples: +- {"client_service_id": "svc-id-1", "server_service_id": "svc-id-2"}: Instance topology for the last 30 minutes +- {"client_service_id": "svc-id-1", "server_service_id": "svc-id-2", "start": "-1h"}: Instance topology for the last hour`, + queryInstancesTopology, + mcp.WithString("client_service_id", + mcp.Required(), + mcp.Description("The ID of the client (upstream) service.")), + mcp.WithString("server_service_id", + mcp.Required(), + mcp.Description("The ID of the server (downstream) service.")), + mcp.WithString("start", mcp.Description("Start time for the query.")), + mcp.WithString("end", mcp.Description("End time for the query. Default is now.")), + mcp.WithString("step", mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), + mcp.Description("Time step granularity. If not specified, uses adaptive step sizing.")), +) + +var ServicesTopologyTool = NewTool( + "query_services_topology", + `Query the service topology from SkyWalking OAP. + +- If service_ids is provided, returns the topology scoped to those specific services (getServicesTopology). +- Otherwise, returns the global topology across all services (getGlobalTopology), optionally filtered by layer. + +Examples: +- {}: Global topology for the last 30 minutes +- {"layer": "GENERAL"}: Global topology for a specific layer +- {"service_ids": ["svc-id-1", "svc-id-2"], "start": "-1h"}: Topology for specific services`, + queryServicesTopology, + mcp.WithArray("service_ids", + mcp.Description("List of service IDs to scope the topology. If empty, the global topology is returned."), + mcp.WithStringItems(), + ), + mcp.WithString("layer", + mcp.Description("Layer to filter the global topology (e.g. GENERAL, MESH). Only used when service_ids is empty.")), + mcp.WithString("start", mcp.Description("Start time for the query.")), + mcp.WithString("end", mcp.Description("End time for the query. Default is now.")), + mcp.WithString("step", mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), + mcp.Description("Time step granularity. If not specified, uses adaptive step sizing.")), +) diff --git a/internal/tools/trace.go b/internal/tools/trace.go index fb5f85b..208a17c 100644 --- a/internal/tools/trace.go +++ b/internal/tools/trace.go @@ -23,20 +23,17 @@ import ( "errors" "fmt" "sort" - "strings" - "time" + "github.com/machinebox/graphql" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" api "skywalking.apache.org/repo/goapi/query" - "github.com/apache/skywalking-cli/pkg/graphql/trace" + "github.com/apache/skywalking-cli/pkg/graphql/client" ) // AddTraceTools registers trace-related tools with the MCP server func AddTraceTools(mcp *server.MCPServer) { - SearchTraceTool.Register(mcp) - ColdTraceTool.Register(mcp) TracesQueryTool.Register(mcp) } @@ -62,22 +59,47 @@ const ( // Error constants const ( - ErrMissingTraceID = "missing required parameter: trace_id" - ErrFailedToQueryTrace = "failed to query trace '%s': %v" - ErrFailedToQueryColdTrace = "failed to query cold trace '%s': %v" - ErrFailedToQueryTraces = "failed to query traces: %v" - ErrNoFilterCondition = "at least one filter condition must be provided" - ErrInvalidDurationRange = "invalid duration range: min_duration (%d) > max_duration (%d)" - ErrNegativePageSize = "page_size cannot be negative" - ErrNegativePageNum = "page_num cannot be negative" - ErrInvalidTraceState = "invalid trace_state '%s', available states: %s, %s, %s" - ErrInvalidQueryOrder = "invalid query_order '%s', available orders: %s, %s" - ErrTraceNotFound = "trace with ID '%s' not found" - ErrInvalidView = "invalid view '%s', available views: %s, %s, %s" - ErrNoTracesFound = "no traces found matching the query criteria" + ErrFailedToQueryTraces = "failed to query traces: %v" + ErrNoFilterCondition = "at least one filter condition must be provided" + ErrInvalidDurationRange = "invalid duration range: min_duration (%d) > max_duration (%d)" + ErrNegativePageSize = "page_size cannot be negative" + ErrNegativePageNum = "page_num cannot be negative" + ErrInvalidTraceState = "invalid trace_state '%s', available states: %s, %s, %s" + ErrInvalidQueryOrder = "invalid query_order '%s', available orders: %s, %s" + ErrInvalidView = "invalid view '%s', available views: %s, %s, %s" + ErrNoTracesFound = "no traces found matching the query criteria" ) -const TimeFormatFull = "2006-01-02 15:04:05" +// queryTracesV2GQL is the GraphQL query for the trace-v2 protocol +const queryTracesV2GQL = ` +query ($condition: TraceQueryCondition) { + result: queryTraces(condition: $condition) { + traces { + spans { + traceId segmentId spanId parentSpanId + refs { traceId parentSegmentId parentSpanId type } + serviceCode serviceInstanceName + startTime endTime endpointName type peer component isError layer + tags { key value } + logs { time data { key value } } + attachedEvents { + startTime { seconds nanos } event endTime { seconds nanos } + tags { key value } summary { key value } + } + } + } + retrievedTimeRange { startTime endTime } + } +}` + +// tracesV2 queries traces using the queryTraces (v2) protocol +func tracesV2(ctx context.Context, condition *api.TraceQueryCondition) (api.TraceList, error) { + var response map[string]api.TraceList + request := graphql.NewRequest(queryTracesV2GQL) + request.Var("condition", condition) + err := client.ExecuteQuery(ctx, request, &response) + return response["result"], err +} // Trace-specific constants const ( @@ -85,19 +107,6 @@ const ( DefaultTraceDuration = "1h" ) -// TraceRequest defines the parameters for the trace tool -type TraceRequest struct { - TraceID string `json:"trace_id"` - View string `json:"view,omitempty"` -} - -// ColdTraceRequest defines the parameters for the cold trace tool -type ColdTraceRequest struct { - TraceID string `json:"trace_id"` - Duration string `json:"duration"` - View string `json:"view,omitempty"` -} - // SpanTag represents a span tag for filtering traces type SpanTag struct { Key string `json:"key"` @@ -110,7 +119,9 @@ type TracesQueryRequest struct { ServiceInstanceID string `json:"service_instance_id,omitempty"` TraceID string `json:"trace_id,omitempty"` EndpointID string `json:"endpoint_id,omitempty"` - Duration string `json:"duration,omitempty"` + Start string `json:"start,omitempty"` + End string `json:"end,omitempty"` + Step string `json:"step,omitempty"` MinTraceDuration int64 `json:"min_trace_duration,omitempty"` MaxTraceDuration int64 `json:"max_trace_duration,omitempty"` TraceState string `json:"trace_state,omitempty"` @@ -123,19 +134,6 @@ type TracesQueryRequest struct { Cold bool `json:"cold,omitempty"` } -// TraceSummary provides a high-level overview of a trace -type TraceSummary struct { - TraceID string `json:"trace_id"` - TotalSpans int `json:"total_spans"` - Services []string `json:"services"` - TotalDuration int64 `json:"total_duration_ms"` - ErrorCount int `json:"error_count"` - HasErrors bool `json:"has_errors"` - RootEndpoint string `json:"root_endpoint"` - StartTime int64 `json:"start_time_ms"` - EndTime int64 `json:"end_time_ms"` -} - // TracesSummary provides a high-level overview of multiple traces type TracesSummary struct { TotalTraces int `json:"total_traces"` @@ -169,155 +167,69 @@ type TimeRange struct { Duration int64 `json:"duration_ms"` } -// createBasicTraceSummary creates a BasicTraceSummary from trace item data -func createBasicTraceSummary(traceItem *api.BasicTrace, startTimeMs, duration int64, isError bool) BasicTraceSummary { - return BasicTraceSummary{ - TraceID: traceItem.TraceIds[0], // Use first trace ID - ServiceName: traceItem.SegmentID, // Use segment ID as service name - EndpointName: strings.Join(traceItem.EndpointNames, ", "), - StartTime: startTimeMs, - Duration: duration, - IsError: isError, - SpanCount: 0, // BasicTrace doesn't have span count - } -} - -// processTraceResult handles the common logic for processing trace results -func processTraceResult(traceID string, traceData *api.Trace, view string) (*mcp.CallToolResult, error) { - if len(traceData.Spans) == 0 { - return mcp.NewToolResultError(fmt.Sprintf(ErrTraceNotFound, traceID)), nil - } - - var result interface{} - switch view { - case ViewSummary: - result = generateTraceSummary(traceID, traceData) - case ViewErrorsOnly: - result = filterErrorSpans(traceData) - case ViewFull: - result = traceData - default: - return mcp.NewToolResultError(fmt.Sprintf(ErrInvalidView, view, ViewFull, ViewSummary, ViewErrorsOnly)), nil - } - - jsonBytes, err := json.Marshal(result) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf(ErrMarshalFailed, err)), nil - } - return mcp.NewToolResultText(string(jsonBytes)), nil -} - -// validateTraceRequest validates trace request parameters -func validateTraceRequest(req TraceRequest) error { - if req.TraceID == "" { - return errors.New(ErrMissingTraceID) - } - return nil -} - -// validateColdTraceRequest validates cold trace request parameters -func validateColdTraceRequest(req ColdTraceRequest) error { - if req.TraceID == "" { - return errors.New(ErrMissingTraceID) - } - if req.Duration == "" { - return errors.New(ErrMissingDuration) - } - return nil -} - -// searchTrace fetches the trace data and processes it based on the requested view -func searchTrace(ctx context.Context, req *TraceRequest) (*mcp.CallToolResult, error) { - if err := validateTraceRequest(*req); err != nil { - return mcp.NewToolResultError(err.Error()), nil - } - if req.View == "" { - req.View = ViewFull // Set default value - } - - traces, err := trace.Trace(ctx, req.TraceID) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf(ErrFailedToQueryTrace, req.TraceID, err)), nil - } - traceData := &traces - - return processTraceResult(req.TraceID, traceData, req.View) -} - -// searchColdTrace fetches the trace data from cold storage and processes it based on the requested view -func searchColdTrace(ctx context.Context, req *ColdTraceRequest) (*mcp.CallToolResult, error) { - if err := validateColdTraceRequest(*req); err != nil { - return mcp.NewToolResultError(err.Error()), nil - } - if req.View == "" { - req.View = ViewFull // Set default value - } - - // Parse duration string to api.Duration - duration := ParseDuration(req.Duration, true) - - traces, err := trace.ColdTrace(ctx, duration, req.TraceID) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf(ErrFailedToQueryColdTrace, req.TraceID, err)), nil - } - traceData := &traces - - return processTraceResult(req.TraceID, traceData, req.View) +type spanStats struct { + traceID string + rootSpan *api.Span + startTime int64 + endTime int64 + isError bool } -// generateTraceSummary creates a summary view from full trace data -func generateTraceSummary(traceID string, traceData *api.Trace) *TraceSummary { - summary := &TraceSummary{ - TraceID: traceID, - TotalSpans: len(traceData.Spans), - } - services := make(map[string]struct{}) - - for _, span := range traceData.Spans { +func collectSpanStats(spans []*api.Span) spanStats { + var s spanStats + for _, span := range spans { if span == nil { continue } - services[span.ServiceCode] = struct{}{} - if span.IsError != nil && *span.IsError { - summary.ErrorCount++ + if span.SpanID == 0 && span.ParentSpanID == -1 && s.rootSpan == nil { + s.rootSpan = span } - // Heuristic to find the root span: a span with spanId 0 and parentSpanId -1 - if span.SpanID == 0 && span.ParentSpanID == -1 { - if span.EndpointName != nil { - summary.RootEndpoint = *span.EndpointName - } - summary.StartTime = span.StartTime - summary.EndTime = span.EndTime - if summary.StartTime > 0 && summary.EndTime > 0 { - summary.TotalDuration = summary.EndTime - summary.StartTime - } + if s.traceID == "" { + s.traceID = span.TraceID + } + if s.startTime == 0 || span.StartTime < s.startTime { + s.startTime = span.StartTime + } + if span.EndTime > s.endTime { + s.endTime = span.EndTime + } + if span.IsError != nil && *span.IsError { + s.isError = true } } - - summary.HasErrors = summary.ErrorCount > 0 - for service := range services { - summary.Services = append(summary.Services, service) - } - sort.Strings(summary.Services) // Ensure deterministic order - return summary + return s } -// filterErrorSpans extracts only the spans with errors from full trace data -func filterErrorSpans(traceData *api.Trace) []*api.Span { - var errorSpans []*api.Span - for _, span := range traceData.Spans { - if span != nil && span.IsError != nil && *span.IsError { - errorSpans = append(errorSpans, span) - } +// createBasicTraceSummary creates a BasicTraceSummary from a TraceV2 +func createBasicTraceSummary(traceItem *api.TraceV2) BasicTraceSummary { + if traceItem == nil || len(traceItem.Spans) == 0 { + return BasicTraceSummary{} + } + stats := collectSpanStats(traceItem.Spans) + rootSpan := stats.rootSpan + if rootSpan == nil { + rootSpan = traceItem.Spans[0] + } + endpointName := "" + if rootSpan.EndpointName != nil { + endpointName = *rootSpan.EndpointName + } + return BasicTraceSummary{ + TraceID: stats.traceID, + ServiceName: rootSpan.ServiceCode, + EndpointName: endpointName, + StartTime: stats.startTime, + Duration: stats.endTime - stats.startTime, + IsError: stats.isError, + SpanCount: len(traceItem.Spans), } - return errorSpans } // validateTracesQueryRequest validates traces query request parameters func validateTracesQueryRequest(req *TracesQueryRequest) error { // At least one filter should be provided for meaningful results if req.ServiceID == "" && req.ServiceInstanceID == "" && req.TraceID == "" && - req.EndpointID == "" && req.Duration == "" && req.MinTraceDuration == 0 && + req.EndpointID == "" && req.Start == "" && req.End == "" && req.MinTraceDuration == 0 && req.MaxTraceDuration == 0 { return errors.New(ErrNoFilterCondition) } @@ -352,6 +264,7 @@ func setBasicFields(req *TracesQueryRequest, condition *api.TraceQueryCondition) if req.EndpointID != "" { condition.EndpointID = &req.EndpointID } + if req.MinTraceDuration > 0 { minDuration := int(req.MinTraceDuration) condition.MinTraceDuration = &minDuration @@ -377,14 +290,16 @@ func setTags(req *TracesQueryRequest, condition *api.TraceQueryCondition) { } // setDuration sets duration in the query condition -func setDuration(req *TracesQueryRequest, condition *api.TraceQueryCondition) { - if req.Duration != "" { - duration := ParseDuration(req.Duration, req.Cold) +func setDuration(req *TracesQueryRequest, condition *api.TraceQueryCondition, timeCtx TimeContext) { + if req.Start != "" || req.End != "" { + duration := BuildDurationWithContext(req.Start, req.End, req.Step, req.Cold, 60, timeCtx) condition.QueryDuration = &duration - } else if req.TraceID == "" { - // If no duration and no traceId provided, set default duration (last 1 hour) + return + } + if req.TraceID == "" { + // If no time range and no traceId provided, set default duration (last 1 hour) // SkyWalking OAP requires either queryDuration or traceId - defaultDuration := ParseDuration(DefaultTraceDuration, req.Cold) + defaultDuration := ParseDurationWithContext(DefaultTraceDuration, req.Cold, timeCtx) condition.QueryDuration = &defaultDuration } } @@ -429,7 +344,7 @@ func setPagination(req *TracesQueryRequest, condition *api.TraceQueryCondition) } // buildQueryCondition builds the query condition from request parameters -func buildQueryCondition(req *TracesQueryRequest) (*api.TraceQueryCondition, error) { +func buildQueryCondition(req *TracesQueryRequest, timeCtx TimeContext) (*api.TraceQueryCondition, error) { condition := &api.TraceQueryCondition{ TraceState: api.TraceStateAll, // Default to all traces QueryOrder: api.QueryOrderByStartTime, // Default order @@ -442,7 +357,7 @@ func buildQueryCondition(req *TracesQueryRequest) (*api.TraceQueryCondition, err setTags(req, condition) // Set duration - setDuration(req, condition) + setDuration(req, condition, timeCtx) // Set trace state if err := setTraceState(req, condition); err != nil { @@ -472,22 +387,23 @@ func searchTraces(ctx context.Context, req *TracesQueryRequest) (*mcp.CallToolRe } // Build query condition - condition, err := buildQueryCondition(req) + timeCtx := GetTimeContext(ctx) + condition, err := buildQueryCondition(req, timeCtx) if err != nil { return mcp.NewToolResultError(err.Error()), nil } - // Execute query - traces, err := trace.Traces(ctx, condition) + // Execute query using queryTraces (v2 protocol) + traceList, err := tracesV2(ctx, condition) if err != nil { return mcp.NewToolResultError(fmt.Sprintf(ErrFailedToQueryTraces, err)), nil } - return processTracesResult(&traces, req.View, req.SlowTraceThreshold) + return processTracesResult(&traceList, req.View, req.SlowTraceThreshold) } // processTracesResult handles the common logic for processing traces query results -func processTracesResult(traces *api.TraceBrief, view string, slowTraceThreshold int64) (*mcp.CallToolResult, error) { +func processTracesResult(traces *api.TraceList, view string, slowTraceThreshold int64) (*mcp.CallToolResult, error) { if traces == nil || len(traces.Traces) == 0 { return mcp.NewToolResultError(ErrNoTracesFound), nil } @@ -511,55 +427,54 @@ func processTracesResult(traces *api.TraceBrief, view string, slowTraceThreshold return mcp.NewToolResultText(string(jsonBytes)), nil } -// processTraceItem processes a single trace item and updates summary statistics -func processTraceItem(traceItem *api.BasicTrace, summary *TracesSummary, +// processTraceItem processes a single TraceV2 item and updates summary statistics +func processTraceItem(traceItem *api.TraceV2, summary *TracesSummary, services, endpoints map[string]struct{}, durations *[]int64, errorTraces, slowTraces *[]BasicTraceSummary, slowTraceThreshold int64, minStartTime, maxEndTime *int64, totalDuration *int64) { - if traceItem == nil { + if traceItem == nil || len(traceItem.Spans) == 0 { return } - // Parse start time - startTime, err := time.Parse(TimeFormatFull, traceItem.Start) - if err != nil { - return // Skip invalid traces + basic := createBasicTraceSummary(traceItem) + if basic.TraceID == "" { + return } - startTimeMs := startTime.UnixMilli() - endTimeMs := startTimeMs + int64(traceItem.Duration) + + endTimeMs := basic.StartTime + basic.Duration // Track time range - if *minStartTime == 0 || startTimeMs < *minStartTime { - *minStartTime = startTimeMs + if *minStartTime == 0 || basic.StartTime < *minStartTime { + *minStartTime = basic.StartTime } if endTimeMs > *maxEndTime { *maxEndTime = endTimeMs } - // Calculate duration - duration := int64(traceItem.Duration) - *durations = append(*durations, duration) - *totalDuration += duration + *durations = append(*durations, basic.Duration) + *totalDuration += basic.Duration - // Count errors - isError := traceItem.IsError != nil && *traceItem.IsError - if isError { + if basic.IsError { summary.ErrorCount++ - *errorTraces = append(*errorTraces, createBasicTraceSummary(traceItem, startTimeMs, duration, true)) + *errorTraces = append(*errorTraces, basic) } else { summary.SuccessCount++ } - // Identify slow traces only if threshold is configured - if slowTraceThreshold > 0 && duration > slowTraceThreshold { - *slowTraces = append(*slowTraces, createBasicTraceSummary(traceItem, startTimeMs, duration, isError)) + if slowTraceThreshold > 0 && basic.Duration > slowTraceThreshold { + *slowTraces = append(*slowTraces, basic) } - // Collect services and endpoints - services[traceItem.SegmentID] = struct{}{} - for _, endpoint := range traceItem.EndpointNames { - if endpoint != "" { - endpoints[endpoint] = struct{}{} + // Collect services and endpoints from all spans + for _, span := range traceItem.Spans { + if span == nil { + continue + } + if span.ServiceCode != "" { + services[span.ServiceCode] = struct{}{} + } + if span.EndpointName != nil && *span.EndpointName != "" { + endpoints[*span.EndpointName] = struct{}{} } } } @@ -582,7 +497,7 @@ func calculateStatistics(durations []int64, totalDuration int64) (avgDuration fl } // generateTracesSummary creates a comprehensive summary from multiple traces -func generateTracesSummary(traces *api.TraceBrief, slowTraceThreshold int64) *TracesSummary { +func generateTracesSummary(traces *api.TraceList, slowTraceThreshold int64) *TracesSummary { if traces == nil || len(traces.Traces) == 0 { return &TracesSummary{} } @@ -641,23 +556,19 @@ func generateTracesSummary(traces *api.TraceBrief, slowTraceThreshold int64) *Tr } // filterErrorTraces extracts only error traces from the results -func filterErrorTraces(traces *api.TraceBrief) []BasicTraceSummary { +func filterErrorTraces(traces *api.TraceList) []BasicTraceSummary { if traces == nil { return nil } var errorTraces []BasicTraceSummary for _, traceItem := range traces.Traces { - if traceItem != nil && traceItem.IsError != nil && *traceItem.IsError { - // Parse start time - startTime, err := time.Parse(TimeFormatFull, traceItem.Start) - if err != nil { - continue - } - startTimeMs := startTime.UnixMilli() - - errorTraces = append(errorTraces, - createBasicTraceSummary(traceItem, startTimeMs, int64(traceItem.Duration), true)) + if traceItem == nil { + continue + } + basic := createBasicTraceSummary(traceItem) + if basic.IsError { + errorTraces = append(errorTraces, basic) } } @@ -669,96 +580,15 @@ func filterErrorTraces(traces *api.TraceBrief) []BasicTraceSummary { return errorTraces } -// SearchTraceTool is a tool for searching traces by trace ID with different views -var SearchTraceTool = NewTool[TraceRequest, *mcp.CallToolResult]( - "get_trace_details", - `This tool provides detailed information about a distributed trace from SkyWalking OAP. - -Workflow: -1. Use this tool when you need to analyze a specific trace by its trace ID -2. Choose the appropriate view based on your analysis needs: - - 'full': For complete trace analysis with all spans and details - - 'summary': For quick overview and performance metrics - - 'errors_only': For troubleshooting and error investigation - -Best Practices: -- Use 'summary' view first to get an overview of the trace -- Switch to 'errors_only' if the summary shows errors -- Use 'full' view for detailed debugging and span-by-span analysis -- Trace IDs are typically found in logs, error messages, or monitoring dashboards - -Examples: -- {"trace_id": "abc123..."}: Get complete trace details for analysis -- {"trace_id": "abc123...", "view": "summary"}: Quick performance overview -- {"trace_id": "abc123...", "view": "errors_only"}: Focus on error spans only`, - searchTrace, - mcp.WithTitleAnnotation("Search a trace by TraceId"), - mcp.WithString("trace_id", mcp.Required(), - mcp.Description(`The unique identifier of the trace to retrieve.`), - ), - mcp.WithString("view", - mcp.Enum(ViewFull, ViewSummary, ViewErrorsOnly), - mcp.Description(`Specifies the level of detail for trace analysis: -- 'full': (Default) Complete trace with all spans, service calls, and metadata -- 'summary': High-level overview with services, duration, and error count -- 'errors_only': Only spans marked as errors for troubleshooting`), - ), -) - -// ColdTraceTool is a tool for searching traces from cold storage by trace ID with different views -var ColdTraceTool = NewTool[ColdTraceRequest, *mcp.CallToolResult]( - "get_cold_trace_details", - `This tool queries BanyanDB cold storage for historical trace data that may no longer be available in hot storage. - -Important Notes: -- Only works with BanyanDB storage backend -- Queries older trace data that has been moved to cold storage -- May have slower response times compared to hot storage queries -- Use when trace data is not found in regular trace queries - -Duration Format: -- Standard Go duration: "7d", "1h", "-30m", "2h30m" -- Negative values mean "ago": "-7d" = 7 days ago to now -- Positive values mean "from now": "2h" = now to 2 hours later -- Legacy format: "6d", "12h" (backward compatible) - -Usage Scenarios: -- Historical incident investigation -- Long-term performance analysis -- Compliance and audit requirements -- When hot storage queries return no results - -Examples: -- {"trace_id": "abc123...", "duration": "7d"}: Search last 7 days of cold storage -- {"trace_id": "abc123...", "duration": "-30m"}: Search from 30 minutes ago to now -- {"trace_id": "abc123...", "duration": "1h", "view": "summary"}: Quick summary from last hour -- {"trace_id": "abc123...", "duration": "2h30m", "view": "errors_only"}: Error analysis from last 2.5 hours`, - searchColdTrace, - mcp.WithTitleAnnotation("Search a cold trace by TraceId"), - mcp.WithString("trace_id", mcp.Required(), - mcp.Description(`The unique identifier of the trace to retrieve from cold storage. Use this when regular trace queries return no results.`), - ), - mcp.WithString("duration", mcp.Required(), - mcp.Description(`Time duration for cold storage query. Examples: "7d" (last 7 days), "-30m" (last 30 minutes), "2h30m" (last 2.5 hours)`), - ), - mcp.WithString("view", - mcp.Enum(ViewFull, ViewSummary, ViewErrorsOnly), - mcp.Description(`Specifies the level of detail for cold trace analysis: -- 'full': (Default) Complete trace with all spans from cold storage -- 'summary': High-level overview with services, duration, and error count -- 'errors_only': Only error spans for focused troubleshooting`), - ), -) - // TracesQueryTool is a tool for querying traces with various conditions -var TracesQueryTool = NewTool[TracesQueryRequest, *mcp.CallToolResult]( +var TracesQueryTool = NewTool( "query_traces", `This tool queries traces from SkyWalking OAP based on various conditions and provides intelligent data processing for LLM analysis. Workflow: 1. Use this tool when you need to find traces matching specific criteria 2. Specify one or more query conditions to narrow down results -3. Use duration to limit the time range for the search +3. Use start/end to limit the time range for the search 4. Choose the appropriate view for your analysis needs Query Conditions: @@ -766,7 +596,7 @@ Query Conditions: - service_instance_id: Filter by specific service instance - trace_id: Search for a specific trace ID - endpoint_id: Filter by specific endpoint -- duration: Time range for the query (e.g., "1h", "7d", "-30m") +- start/end: Time range for the query (e.g., start "-1h", end "now") - min_trace_duration/max_trace_duration: Filter by trace duration in milliseconds - trace_state: Filter by trace state (success, error, all) - query_order: Sort order (start_time, duration, start_time_desc, duration_desc) @@ -775,8 +605,8 @@ Query Conditions: - tags: Filter by span tags (key-value pairs) Important Notes: -- SkyWalking OAP requires either 'duration' or 'trace_id' to be specified -- If neither is provided, a default duration of "1h" (last 1 hour) will be used +- SkyWalking OAP requires either a time range or 'trace_id' to be specified +- If no time range and no trace_id are provided, a default duration of "1h" (last 1 hour) will be used - This ensures the query always has a valid time range or specific trace to search View Options: @@ -788,18 +618,18 @@ Best Practices: - Start with 'summary' view to get an intelligent overview - Use 'errors_only' view for focused troubleshooting - Combine multiple filters for precise results -- Use duration to limit search scope and improve performance +- Use time ranges to limit search scope and improve performance - Only set slow_trace_threshold when you need to identify performance issues - Use tags to filter traces by specific attributes or metadata Examples: -- {"service_id": "Your_ApplicationName", "duration": "1h", "view": "summary"}: Recent traces summary with performance insights -- {"trace_state": "error", "duration": "7d", "view": "errors_only"}: Error traces from last week for troubleshooting +- {"service_id": "Your_ApplicationName", "start": "-1h", "end": "now", "view": "summary"}: Recent traces summary with performance insights +- {"trace_state": "error", "start": "-7d", "end": "now", "view": "errors_only"}: Error traces from last week for troubleshooting - {"min_trace_duration": 1000, "query_order": "duration_desc", "view": "summary"}: Slow traces analysis with performance metrics - {"slow_trace_threshold": 5000, "view": "summary"}: Identify traces slower than 5 seconds - {"service_id": "Your_ApplicationName"}: Query with default 1-hour duration - {"tags": [{"key": "http.method", "value": "POST"}, {"key": "http.status_code", "value": "500"}], - "duration": "1h"}: Find traces with specific HTTP tags`, + "start": "-1h", "end": "now"}: Find traces with specific HTTP tags`, searchTraces, mcp.WithTitleAnnotation("Query traces with intelligent analysis"), mcp.WithString("service_id", @@ -814,8 +644,16 @@ Examples: mcp.WithString("endpoint_id", mcp.Description("Endpoint ID to filter traces. Use this to find traces for a specific endpoint."), ), - mcp.WithString("duration", - mcp.Description(`Time duration for the query. Examples: "7d" (last 7 days), "-30m" (last 30 minutes), "2h30m" (last 2.5 hours)`), + mcp.WithString("start", + mcp.Description("Start time for the query. Examples: \"2023-01-01 12:00:00\", \"-1h\" (1 hour ago), \"-30m\" (30 minutes ago)"), + ), + mcp.WithString("end", + mcp.Description("End time for the query. Examples: \"2023-01-01 13:00:00\", \"now\","+ + " \"-10m\" (10 minutes ago) Defaults to current time if omitted."), + ), + mcp.WithString("step", + mcp.Enum("SECOND", "MINUTE", "HOUR", "DAY"), + mcp.Description("Time step granularity. If not specified, uses adaptive sizing."), ), mcp.WithNumber("min_trace_duration", mcp.Description("Minimum trace duration in milliseconds. Use this to filter out fast traces."), @@ -850,8 +688,16 @@ Examples: "Examples: 500 (0.5s), 2000 (2s), 5000 (5s)"), ), mcp.WithArray("tags", - mcp.Description(`Array of span tags to filter traces. Each tag should have 'key' and 'value' fields. + mcp.Description(`Array of span tags to filter traces. Each tag should have 'key' and 'value' fields. Examples: [{"key": "http.method", "value": "POST"}, {"key": "http.status_code", "value": "500"}]`), + mcp.Items(map[string]any{ + "type": "object", + "properties": map[string]any{ + "key": map[string]any{"type": "string"}, + "value": map[string]any{"type": "string"}, + }, + "required": []string{"key", "value"}, + }), ), mcp.WithBoolean("cold", mcp.Description("Whether to query from cold-stage storage. Set to true for historical data queries."),