mirror of
https://github.com/alibaba/higress.git
synced 2026-06-09 12:47:28 +08:00
feat: implement hgctl agent & mcp add subcommand (#3051)
This commit is contained in:
135
.github/workflows/build-and-test.yaml
vendored
135
.github/workflows/build-and-test.yaml
vendored
@@ -2,18 +2,20 @@ name: "Build and Test"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["*"]
|
branches: ["*"]
|
||||||
|
|
||||||
|
env:
|
||||||
|
GO_VERSION: 1.24
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.22
|
go-version: ${{ env.GO_VERSION }}
|
||||||
# There are too many lint errors in current code bases
|
# There are too many lint errors in current code bases
|
||||||
# uncomment when we decide what lint should be addressed or ignored.
|
# uncomment when we decide what lint should be addressed or ignored.
|
||||||
# - run: make lint
|
# - run: make lint
|
||||||
@@ -21,40 +23,40 @@ jobs:
|
|||||||
coverage-test:
|
coverage-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: "Setup Go"
|
- name: "Setup Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.22
|
go-version: ${{ env.GO_VERSION }}
|
||||||
|
|
||||||
- name: Setup Golang Caches
|
- name: Setup Golang Caches
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
~/go/pkg/mod
|
~/go/pkg/mod
|
||||||
key: ${{ runner.os }}-go-${{ github.run_id }}
|
key: ${{ runner.os }}-go-${{ github.run_id }}
|
||||||
restore-keys: ${{ runner.os }}-go
|
restore-keys: ${{ runner.os }}-go
|
||||||
|
|
||||||
- run: git stash # restore patch
|
- run: git stash # restore patch
|
||||||
|
|
||||||
# test
|
# test
|
||||||
- name: Run Coverage Tests
|
- name: Run Coverage Tests
|
||||||
run: |-
|
run: |-
|
||||||
go version
|
go version
|
||||||
GOPROXY="https://proxy.golang.org,direct" make go.test.coverage
|
GOPROXY="https://proxy.golang.org,direct" make go.test.coverage
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v4
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: false
|
fail_ci_if_error: false
|
||||||
files: ./coverage.xml
|
files: ./coverage.xml
|
||||||
verbose: true
|
verbose: true
|
||||||
|
|
||||||
build:
|
build:
|
||||||
# The type of runner that the job will run on
|
# The type of runner that the job will run on
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [lint,coverage-test]
|
needs: [lint, coverage-test]
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout ${{ github.ref }}"
|
- name: "Checkout ${{ github.ref }}"
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -64,7 +66,7 @@ jobs:
|
|||||||
- name: "Setup Go"
|
- name: "Setup Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.22
|
go-version: ${{ env.GO_VERSION }}
|
||||||
|
|
||||||
- name: Setup Golang Caches
|
- name: Setup Golang Caches
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
@@ -90,45 +92,52 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build]
|
needs: [build]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
higress-conformance-test:
|
higress-conformance-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build]
|
needs: [build]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Free Up GitHub Actions Ubuntu Runner Disk Space 🔧
|
- name: Free Up GitHub Actions Ubuntu Runner Disk Space 🔧
|
||||||
uses: jlumbroso/free-disk-space@main
|
uses: jlumbroso/free-disk-space@main
|
||||||
with:
|
with:
|
||||||
tool-cache: false
|
tool-cache: false
|
||||||
android: true
|
android: true
|
||||||
dotnet: true
|
dotnet: true
|
||||||
haskell: true
|
haskell: true
|
||||||
large-packages: true
|
large-packages: true
|
||||||
swap-storage: true
|
swap-storage: true
|
||||||
|
|
||||||
- name: "Setup Go"
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: 1.22
|
|
||||||
|
|
||||||
- name: Setup Golang Caches
|
- name: "Setup Go"
|
||||||
uses: actions/cache@v4
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
path: |-
|
go-version: ${{ env.GO_VERSION }}
|
||||||
~/.cache/go-build
|
|
||||||
~/go/pkg/mod
|
- name: Setup Golang Caches
|
||||||
key: ${{ runner.os }}-go-${{ github.run_id }}
|
uses: actions/cache@v4
|
||||||
restore-keys: ${{ runner.os }}-go
|
with:
|
||||||
|
path: |-
|
||||||
- run: git stash # restore patch
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ github.run_id }}
|
||||||
|
# key: ${{ runner.os }}-go-${{ env.GO_VERSION }}
|
||||||
|
|
||||||
|
restore-keys: ${{ runner.os }}-go
|
||||||
|
|
||||||
|
- run: git stash # restore patch
|
||||||
|
|
||||||
|
- name: update go mod
|
||||||
|
run: |-
|
||||||
|
make prebuild
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
|
- name: "Run Higress E2E Conformance Tests"
|
||||||
|
run: GOPROXY="https://proxy.golang.org,direct" make higress-conformance-test
|
||||||
|
|
||||||
- name: "Run Higress E2E Conformance Tests"
|
|
||||||
run: GOPROXY="https://proxy.golang.org,direct" make higress-conformance-test
|
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [higress-conformance-test,gateway-conformance-test]
|
needs: [higress-conformance-test, gateway-conformance-test]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
22
hgctl/go.mod
22
hgctl/go.mod
@@ -1,8 +1,8 @@
|
|||||||
module github.com/alibaba/higress/hgctl
|
module github.com/alibaba/higress/hgctl
|
||||||
|
|
||||||
go 1.22.2
|
go 1.23.0
|
||||||
|
|
||||||
toolchain go1.23.7
|
toolchain go1.24.1
|
||||||
|
|
||||||
replace github.com/spf13/viper => github.com/istio/viper v1.3.3-0.20190515210538-2789fed3109c
|
replace github.com/spf13/viper => github.com/istio/viper v1.3.3-0.20190515210538-2789fed3109c
|
||||||
|
|
||||||
@@ -20,6 +20,7 @@ replace github.com/alibaba/higress/v2 => ../
|
|||||||
require (
|
require (
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||||
github.com/alibaba/higress/v2 v2.0.0-00010101000000-000000000000
|
github.com/alibaba/higress/v2 v2.0.0-00010101000000-000000000000
|
||||||
|
github.com/braydonk/yaml v0.7.0
|
||||||
github.com/compose-spec/compose-go v1.17.0
|
github.com/compose-spec/compose-go v1.17.0
|
||||||
github.com/docker/cli v24.0.7+incompatible
|
github.com/docker/cli v24.0.7+incompatible
|
||||||
github.com/docker/compose/v2 v2.23.3
|
github.com/docker/compose/v2 v2.23.3
|
||||||
@@ -28,6 +29,7 @@ require (
|
|||||||
github.com/fatih/color v1.15.0
|
github.com/fatih/color v1.15.0
|
||||||
github.com/fatih/structtag v1.2.0
|
github.com/fatih/structtag v1.2.0
|
||||||
github.com/google/yamlfmt v0.10.0
|
github.com/google/yamlfmt v0.10.0
|
||||||
|
github.com/higress-group/openapi-to-mcpserver v0.0.0-20250925065334-de60a170f950
|
||||||
github.com/iancoleman/orderedmap v0.3.0
|
github.com/iancoleman/orderedmap v0.3.0
|
||||||
github.com/kylelemons/godebug v1.1.0
|
github.com/kylelemons/godebug v1.1.0
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
@@ -37,7 +39,7 @@ require (
|
|||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/viper v1.16.0
|
github.com/spf13/viper v1.16.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.10.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
helm.sh/helm/v3 v3.12.2
|
helm.sh/helm/v3 v3.12.2
|
||||||
@@ -51,6 +53,8 @@ require (
|
|||||||
sigs.k8s.io/yaml v1.4.0
|
sigs.k8s.io/yaml v1.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||||
@@ -79,7 +83,6 @@ require (
|
|||||||
github.com/aws/smithy-go v1.13.5 // indirect
|
github.com/aws/smithy-go v1.13.5 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect
|
github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect
|
||||||
github.com/braydonk/yaml v0.7.0 // indirect
|
|
||||||
github.com/buger/goterm v1.0.4 // indirect
|
github.com/buger/goterm v1.0.4 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
|
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
|
||||||
@@ -111,13 +114,14 @@ require (
|
|||||||
github.com/fsnotify/fsevents v0.1.1 // indirect
|
github.com/fsnotify/fsevents v0.1.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||||
|
github.com/getkin/kin-openapi v0.118.0 // indirect
|
||||||
github.com/go-errors/errors v1.4.2 // indirect
|
github.com/go-errors/errors v1.4.2 // indirect
|
||||||
github.com/go-gorp/gorp/v3 v3.0.5 // indirect
|
github.com/go-gorp/gorp/v3 v3.0.5 // indirect
|
||||||
github.com/go-logr/logr v1.2.4 // indirect
|
github.com/go-logr/logr v1.2.4 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/swag v0.22.4 // indirect
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
github.com/gobwas/glob v0.2.3 // indirect
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/gofrs/flock v0.8.1 // indirect
|
github.com/gofrs/flock v0.8.1 // indirect
|
||||||
@@ -147,6 +151,7 @@ require (
|
|||||||
github.com/imdario/mergo v1.0.0 // indirect
|
github.com/imdario/mergo v1.0.0 // indirect
|
||||||
github.com/in-toto/in-toto-golang v0.5.0 // indirect
|
github.com/in-toto/in-toto-golang v0.5.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/invopop/yaml v0.1.0 // indirect
|
||||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||||
github.com/jonboulle/clockwork v0.4.0 // indirect
|
github.com/jonboulle/clockwork v0.4.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
@@ -165,6 +170,7 @@ require (
|
|||||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/manifoldco/promptui v0.9.0
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
@@ -187,6 +193,7 @@ require (
|
|||||||
github.com/moby/term v0.5.0 // indirect
|
github.com/moby/term v0.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
@@ -194,6 +201,7 @@ require (
|
|||||||
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
|
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
|
||||||
github.com/opencontainers/runc v1.1.9 // indirect
|
github.com/opencontainers/runc v1.1.9 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||||
|
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_golang v1.17.0 // indirect
|
github.com/prometheus/client_golang v1.17.0 // indirect
|
||||||
@@ -245,7 +253,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.27.0 // indirect
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
golang.org/x/exp v0.0.0-20250717185816-542afb5b7346 // indirect
|
||||||
golang.org/x/mod v0.17.0 // indirect
|
golang.org/x/mod v0.17.0 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/oauth2 v0.13.0 // indirect
|
golang.org/x/oauth2 v0.13.0 // indirect
|
||||||
|
|||||||
37
hgctl/go.sum
37
hgctl/go.sum
@@ -752,6 +752,7 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
|||||||
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
|
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
|
||||||
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
|
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
|
||||||
github.com/chzyer/logex v1.1.11-0.20170329064859-445be9e134b2/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.11-0.20170329064859-445be9e134b2/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
@@ -910,6 +911,8 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT
|
|||||||
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
|
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
|
||||||
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||||
|
github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM=
|
||||||
|
github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
|
||||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
@@ -954,9 +957,10 @@ github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwds
|
|||||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||||
github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
|
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||||
github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
|
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||||
@@ -987,8 +991,8 @@ github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/
|
|||||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||||
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||||
@@ -1000,6 +1004,8 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
|
|||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
|
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||||
|
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||||
github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU=
|
github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU=
|
||||||
github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs=
|
github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs=
|
||||||
@@ -1211,6 +1217,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.4 h1:7GHuZcgid37q8o5i3QI9KMT4nCWQQ3Kx3Ov
|
|||||||
github.com/hashicorp/golang-lru/v2 v2.0.4/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
github.com/hashicorp/golang-lru/v2 v2.0.4/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/higress-group/openapi-to-mcpserver v0.0.0-20250925065334-de60a170f950 h1:a3/hCNZednJoFbp1DPx2O/LRUwvcsyeTpL0MP+qIApg=
|
||||||
|
github.com/higress-group/openapi-to-mcpserver v0.0.0-20250925065334-de60a170f950/go.mod h1:jRTljni4fNs7aLiAbOhAAWIjctA4NSNtm5z7kGimG6U=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
@@ -1231,6 +1239,8 @@ github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1Gd
|
|||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
|
||||||
|
github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
|
||||||
github.com/istio/viper v1.3.3-0.20190515210538-2789fed3109c h1:EFWADU43GY2T7NIYYbIHWdrG2hRiWyGSHeON57ZADBE=
|
github.com/istio/viper v1.3.3-0.20190515210538-2789fed3109c h1:EFWADU43GY2T7NIYYbIHWdrG2hRiWyGSHeON57ZADBE=
|
||||||
github.com/istio/viper v1.3.3-0.20190515210538-2789fed3109c/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
github.com/istio/viper v1.3.3-0.20190515210538-2789fed3109c/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
@@ -1337,6 +1347,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
|
|||||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||||
|
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||||
github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI=
|
github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI=
|
||||||
github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=
|
github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=
|
||||||
github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY=
|
github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY=
|
||||||
@@ -1425,6 +1437,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
||||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
@@ -1481,6 +1495,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
|
|||||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
|
github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||||
|
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||||
|
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||||
@@ -1623,8 +1640,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
@@ -1634,8 +1652,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
@@ -1648,7 +1667,11 @@ github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/
|
|||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
||||||
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 h1:Y/M5lygoNPKwVNLMPXgVfsRT40CSFKXCxuU8LoHySjs=
|
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 h1:Y/M5lygoNPKwVNLMPXgVfsRT40CSFKXCxuU8LoHySjs=
|
||||||
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
|
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
|
||||||
|
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
||||||
|
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
|
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||||
|
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
|
github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
|
||||||
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
|
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
|
||||||
@@ -1972,6 +1995,7 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -2534,6 +2558,7 @@ gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
|
|||||||
46
hgctl/pkg/agent/agent.go
Normal file
46
hgctl/pkg/agent/agent.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) 2025 Alibaba Group Holding 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.
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewAgentCmd() *cobra.Command {
|
||||||
|
agentCmd := &cobra.Command{
|
||||||
|
Use: "agent",
|
||||||
|
Short: "start the interactive agent window",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
cmdutil.CheckErr(handleAgentInvoke(cmd.OutOrStdout()))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return agentCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleAgentInvoke(w io.Writer) error {
|
||||||
|
|
||||||
|
return getAgent().Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub-Agent1:
|
||||||
|
// 1. Parse the url provided by user to MCP server configuration.
|
||||||
|
// 2. Publish the parsed MCP Server to Higress
|
||||||
|
func addPrequisiteSubAgent() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
61
hgctl/pkg/agent/base.go
Normal file
61
hgctl/pkg/agent/base.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) 2025 Alibaba Group Holding 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.
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AgentBinaryName = "claude"
|
||||||
|
BinaryVersion = "0.1.0"
|
||||||
|
DevVersion = "dev"
|
||||||
|
NodeLeastVersion = 18
|
||||||
|
AgentInstallCmd = "npm install -g @anthropic-ai/claude-code"
|
||||||
|
AgentReleasePage = "https://docs.claude.com/en/docs/claude-code/setup"
|
||||||
|
)
|
||||||
|
|
||||||
|
// set up the core env
|
||||||
|
// 1. check if npm is installed
|
||||||
|
// 2. check the npm version
|
||||||
|
// 3. install hgctl-agent
|
||||||
|
func getAgent() *AgenticCore {
|
||||||
|
if !checkAgentInstallStatus() {
|
||||||
|
fmt.Println("⚠️ Prerequisites not satisfied. Exiting...")
|
||||||
|
// exit directly
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewAgenticCore()
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAgentInstallStatus() bool {
|
||||||
|
// TODO: Support cross-platform:windows
|
||||||
|
|
||||||
|
if !checkNodeInstall() {
|
||||||
|
if err := promptNodeInstall(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !checkAgentInstall() {
|
||||||
|
if err := promptAgentInstall(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
46
hgctl/pkg/agent/core.go
Normal file
46
hgctl/pkg/agent/core.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) 2025 Alibaba Group Holding 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.
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AgenticCore struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAgenticCore() *AgenticCore {
|
||||||
|
return &AgenticCore{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AgenticCore) run(args ...string) error {
|
||||||
|
cmd := exec.Command(AgentBinaryName, args...)
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------- Initialization -------
|
||||||
|
func (c *AgenticCore) Start() error {
|
||||||
|
return c.run(AgentBinaryName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------- MCP -------
|
||||||
|
func (c *AgenticCore) AddMCPServer(name string, url string) error {
|
||||||
|
return c.run("mcp", "add", "--transport", HTTP, name, url)
|
||||||
|
}
|
||||||
314
hgctl/pkg/agent/mcp.go
Normal file
314
hgctl/pkg/agent/mcp.go
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
// Copyright (c) 2025 Alibaba Group Holding 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.
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/hgctl/pkg/agent/services"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/higress-group/openapi-to-mcpserver/pkg/models"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MCPType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
HTTP string = "http"
|
||||||
|
SSE string = "sse"
|
||||||
|
OPENAPI string = "openapi"
|
||||||
|
DIRECT_ROUTE string = "DIRECT_ROUTE"
|
||||||
|
OPEN_API string = "OPEN_API"
|
||||||
|
|
||||||
|
HIGRESS_CONSOLE_URL = "higress-console-url"
|
||||||
|
HIGRESS_CONSOLE_USER = "higress-console-user"
|
||||||
|
HIGRESS_CONSOLE_PASSWORD = "higress-console-password"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MCPAddArg struct {
|
||||||
|
// higress console auth arg
|
||||||
|
baseURL string
|
||||||
|
hgUser string
|
||||||
|
hgPassword string
|
||||||
|
|
||||||
|
name string
|
||||||
|
url string
|
||||||
|
transport string
|
||||||
|
spec string
|
||||||
|
scope string
|
||||||
|
noPublish bool
|
||||||
|
// TODO: support mcp env
|
||||||
|
// env string
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type MCPAddHandler struct {
|
||||||
|
core *AgenticCore
|
||||||
|
arg MCPAddArg
|
||||||
|
w io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMCPCmd() *cobra.Command {
|
||||||
|
mcpCmd := &cobra.Command{
|
||||||
|
Use: "mcp",
|
||||||
|
Short: "for the mcp management",
|
||||||
|
}
|
||||||
|
|
||||||
|
mcpCmd.AddCommand(newMCPAddCmd())
|
||||||
|
|
||||||
|
return mcpCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMCPAddCmd() *cobra.Command {
|
||||||
|
// parameter
|
||||||
|
arg := &MCPAddArg{}
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "add [name]",
|
||||||
|
Short: "add mcp server including http and openapi",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
arg.name = args[0]
|
||||||
|
resolveHigressConsoleAuth(arg)
|
||||||
|
cmdutil.CheckErr(handleAddMCP(cmd.OutOrStdout(), *arg))
|
||||||
|
color.Cyan("Tip: Try doing 'kubectl port-forward' and add the server to the agent manually, if MCP Server connection failed")
|
||||||
|
},
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().StringVarP(&arg.transport, "transport", "t", HTTP, "Determine the MCP Server's Type")
|
||||||
|
cmd.PersistentFlags().StringVarP(&arg.url, "url", "u", "", "MCP server URL")
|
||||||
|
cmd.PersistentFlags().StringVarP(&arg.scope, "scope", "s", "project", `Configuration scope (project or global)`)
|
||||||
|
cmd.PersistentFlags().StringVar(&arg.spec, "spec", "", "Specification of the openapi api")
|
||||||
|
cmd.PersistentFlags().BoolVar(&arg.noPublish, "no-publish", false, "If set then the mcp server will not be plubished to higress")
|
||||||
|
|
||||||
|
flagHigressConsoleAuth(cmd, arg)
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHanlder(c *AgenticCore, arg MCPAddArg, w io.Writer) *MCPAddHandler {
|
||||||
|
return &MCPAddHandler{c, arg, w}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MCPAddHandler) validateArg() error {
|
||||||
|
if !h.arg.noPublish {
|
||||||
|
if h.arg.baseURL == "" || h.arg.hgUser == "" || h.arg.hgPassword == "" {
|
||||||
|
fmt.Println("--higress-console-user, --higress-console-url, --higress-console-password must be provided")
|
||||||
|
return fmt.Errorf("invalid args")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MCPAddHandler) addHTTPMCP() error {
|
||||||
|
if err := h.core.AddMCPServer(h.arg.name, h.arg.url); err != nil {
|
||||||
|
return fmt.Errorf("mcp add failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h.arg.noPublish {
|
||||||
|
return publishToHigress(h.arg, nil)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// hgctl mcp add -t openapi --name test-name --spec openapi.json
|
||||||
|
func (h *MCPAddHandler) addOpenAPIMCP() error {
|
||||||
|
// fmt.Printf("get mcp server: %s openapi-spec-file: %s\n", h.arg.name, h.arg.spec)
|
||||||
|
config := h.parseOpenapiSpec()
|
||||||
|
|
||||||
|
// fmt.Printf("get config struct: %v", config)
|
||||||
|
|
||||||
|
// publish to higress
|
||||||
|
if err := publishToHigress(h.arg, config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add mcp server to agent
|
||||||
|
gatewayIP, err := GetHigressGatewayServiceIP()
|
||||||
|
if err != nil {
|
||||||
|
color.Red(
|
||||||
|
"failed to add mcp server [%s] while getting higress-gateway ip due to: %v \n You may try to do port-forward and add it to agent manually", h.arg.name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mcpURL := fmt.Sprintf("http://%s/mcp-servers/%s", gatewayIP, h.arg.name)
|
||||||
|
return h.core.AddMCPServer(h.arg.name, mcpURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MCPAddHandler) parseOpenapiSpec() *models.MCPConfig {
|
||||||
|
return parseOpenapi2MCP(h.arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleAddMCP(w io.Writer, arg MCPAddArg) error {
|
||||||
|
client := getAgent()
|
||||||
|
h := newHanlder(client, arg, w)
|
||||||
|
if err := h.validateArg(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec -> OPENAPI
|
||||||
|
// noPublish -> typ
|
||||||
|
switch arg.transport {
|
||||||
|
case HTTP:
|
||||||
|
return h.addHTTPMCP()
|
||||||
|
case OPENAPI:
|
||||||
|
if arg.spec == "" {
|
||||||
|
return fmt.Errorf("--spec is required for openapi type")
|
||||||
|
}
|
||||||
|
if arg.noPublish {
|
||||||
|
return fmt.Errorf("--no-publish is not supported for openapi type")
|
||||||
|
}
|
||||||
|
if arg.url != "" {
|
||||||
|
return fmt.Errorf("--url is not supported for openapi type")
|
||||||
|
}
|
||||||
|
return h.addOpenAPIMCP()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported mcp type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func publishToHigress(arg MCPAddArg, config *models.MCPConfig) error {
|
||||||
|
// 1. parse the raw http url
|
||||||
|
// 2. add service source
|
||||||
|
// 3. add MCP server request
|
||||||
|
client := services.NewHigressClient(arg.baseURL, arg.hgUser, arg.hgPassword)
|
||||||
|
|
||||||
|
// mcp server's url
|
||||||
|
rawURL := arg.url
|
||||||
|
// DIRECT_ROUTE or OPEN_API
|
||||||
|
mcpType := DIRECT_ROUTE
|
||||||
|
|
||||||
|
if config != nil {
|
||||||
|
// TODO: here use tools's url directly, need to be considered
|
||||||
|
rawURL = config.Tools[0].RequestTemplate.URL
|
||||||
|
mcpType = OPEN_API
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := url.Parse(rawURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add service source
|
||||||
|
srvType := ""
|
||||||
|
srvPort := ""
|
||||||
|
srvName := fmt.Sprintf("hgctl-%s", arg.name)
|
||||||
|
srvPath := res.Path
|
||||||
|
|
||||||
|
if ip := net.ParseIP(res.Hostname()); ip == nil {
|
||||||
|
srvType = "dns"
|
||||||
|
} else {
|
||||||
|
srvType = "static"
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Port() == "" && res.Scheme == "http" {
|
||||||
|
srvPort = "80"
|
||||||
|
} else if res.Port() == "" && res.Scheme == "https" {
|
||||||
|
srvPort = "443"
|
||||||
|
} else {
|
||||||
|
srvPort = res.Port()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = services.HandleAddServiceSource(client, map[string]interface{}{
|
||||||
|
"domain": res.Host,
|
||||||
|
"type": srvType,
|
||||||
|
"port": srvPort,
|
||||||
|
"name": srvName,
|
||||||
|
"domainForEdit": res.Host,
|
||||||
|
"protocol": res.Scheme,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
srvField := []map[string]interface{}{{
|
||||||
|
"name": fmt.Sprintf("%s.%s", srvName, srvType),
|
||||||
|
"port": srvPort,
|
||||||
|
"version": "1.0",
|
||||||
|
"weight": 100,
|
||||||
|
}}
|
||||||
|
|
||||||
|
// generete mcp server add request body
|
||||||
|
body := map[string]interface{}{
|
||||||
|
"name": arg.name,
|
||||||
|
// "description": "",
|
||||||
|
"type": mcpType,
|
||||||
|
"service": fmt.Sprintf("%s.%s:%s", srvName, srvType, srvPort),
|
||||||
|
"upstreamPathPrefix": srvPath,
|
||||||
|
"services": srvField,
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("request body: %v", body)
|
||||||
|
|
||||||
|
_, err = services.HandleAddMCPServer(client, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mcpType == OPEN_API {
|
||||||
|
addMCPToolConfig(client, config, srvField)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addMCPToolConfig(client *services.HigressClient, config *models.MCPConfig, srvField []map[string]interface{}) {
|
||||||
|
body := map[string]interface{}{
|
||||||
|
"name": config.Server.Name,
|
||||||
|
// "description": "",
|
||||||
|
"services": srvField,
|
||||||
|
"type": OPEN_API,
|
||||||
|
"rawConfigurations": convertMCPConfigToStr(config),
|
||||||
|
"mcpServerName": config.Server.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := services.HandleAddOpenAPITool(client, body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("add openapi tools failed: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
// fmt.Println("get openapi tools add response: ", string(resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagHigressConsoleAuth(cmd *cobra.Command, arg *MCPAddArg) {
|
||||||
|
cmd.PersistentFlags().StringVar(&arg.baseURL, HIGRESS_CONSOLE_URL, "", "The BaseURL of higress console")
|
||||||
|
cmd.PersistentFlags().StringVar(&arg.hgUser, HIGRESS_CONSOLE_USER, "", "The username of higress console")
|
||||||
|
cmd.PersistentFlags().StringVarP(&arg.hgPassword, HIGRESS_CONSOLE_PASSWORD, "p", "", "The password of higress console")
|
||||||
|
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||||
|
viper.AutomaticEnv()
|
||||||
|
// TODO: if higress is installed by hgctl, then try to resolve auth arg in install profile
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve from viper
|
||||||
|
func resolveHigressConsoleAuth(arg *MCPAddArg) {
|
||||||
|
if arg.baseURL == "" {
|
||||||
|
arg.baseURL = viper.GetString(HIGRESS_CONSOLE_URL)
|
||||||
|
}
|
||||||
|
if arg.hgUser == "" {
|
||||||
|
arg.hgUser = viper.GetString(HIGRESS_CONSOLE_USER)
|
||||||
|
}
|
||||||
|
if arg.hgPassword == "" {
|
||||||
|
arg.hgPassword = viper.GetString(HIGRESS_CONSOLE_PASSWORD)
|
||||||
|
}
|
||||||
|
}
|
||||||
113
hgctl/pkg/agent/services/client.go
Normal file
113
hgctl/pkg/agent/services/client.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// Copyright (c) 2025 Alibaba Group Holding 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.
|
||||||
|
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HigressClient struct {
|
||||||
|
baseURL string
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHigressClient(baseURL, username, password string) *HigressClient {
|
||||||
|
client := &HigressClient{
|
||||||
|
baseURL: baseURL,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
httpClient: &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HigressClient) Get(path string) ([]byte, error) {
|
||||||
|
return c.request("GET", path, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HigressClient) Post(path string, data interface{}) ([]byte, error) {
|
||||||
|
return c.request("POST", path, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HigressClient) Put(path string, data interface{}) ([]byte, error) {
|
||||||
|
return c.request("PUT", path, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HigressClient) Delete(path string) ([]byte, error) {
|
||||||
|
return c.request("DELETE", path, nil)
|
||||||
|
}
|
||||||
|
func (c *HigressClient) request(method, path string, data interface{}) ([]byte, error) {
|
||||||
|
url := c.baseURL + path
|
||||||
|
|
||||||
|
var body io.Reader
|
||||||
|
if data != nil {
|
||||||
|
jsonData, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to marshal request data: %w", err)
|
||||||
|
}
|
||||||
|
body = bytes.NewBuffer(jsonData)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, method, url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.SetBasicAuth(c.username, c.password)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
resp, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("request failed: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == 409 {
|
||||||
|
return nil, fmt.Errorf("resource already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == 400 {
|
||||||
|
return nil, fmt.Errorf("invalid resource definition")
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == 500 {
|
||||||
|
return nil, fmt.Errorf("server internal error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
return nil, fmt.Errorf("HTTP error %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBody, nil
|
||||||
|
}
|
||||||
129
hgctl/pkg/agent/services/service.go
Normal file
129
hgctl/pkg/agent/services/service.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
// Copyright (c) 2025 Alibaba Group Holding 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.
|
||||||
|
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleAddServiceSource(client *HigressClient, body interface{}) ([]byte, error) {
|
||||||
|
data, ok := body.(map[string]interface{})
|
||||||
|
// fmt.Printf("request body: %v\n", data)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to parse request body")
|
||||||
|
}
|
||||||
|
// Validate
|
||||||
|
if _, ok := data["name"]; !ok {
|
||||||
|
return nil, fmt.Errorf("missing required field 'name' in body")
|
||||||
|
}
|
||||||
|
if _, ok := data["type"]; !ok {
|
||||||
|
return nil, fmt.Errorf("missing required field 'type' in body")
|
||||||
|
}
|
||||||
|
if _, ok := data["domain"]; !ok {
|
||||||
|
return nil, fmt.Errorf("missing required field 'domain' in body")
|
||||||
|
}
|
||||||
|
if _, ok := data["port"]; !ok {
|
||||||
|
return nil, fmt.Errorf("missing required field 'port' in body")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Post("/v1/service-sources", data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to add service source: %w", err)
|
||||||
|
}
|
||||||
|
// res := make(map[string]interface{})
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// add MCP server to higress console, example request body as followed:
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "name": "mcp-deepwiki",
|
||||||
|
// "description": "",
|
||||||
|
// "type": "DIRECT_ROUTE", // or OPEN_API
|
||||||
|
// "service": "hgctl-deepwiki.dns:443",
|
||||||
|
// "upstreamPathPrefix": "/mcp",
|
||||||
|
// "services": [
|
||||||
|
// {
|
||||||
|
// "name": "hgctl-deepwiki.dns",
|
||||||
|
// "port": 443,
|
||||||
|
// "version": "1.0",
|
||||||
|
// "weight": 100
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
func HandleAddMCPServer(client *HigressClient, body interface{}) ([]byte, error) {
|
||||||
|
data, ok := body.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to parse request body")
|
||||||
|
}
|
||||||
|
// Validate
|
||||||
|
if _, ok := data["name"]; !ok {
|
||||||
|
return nil, fmt.Errorf("missing required field 'name' in body")
|
||||||
|
}
|
||||||
|
if _, ok := data["type"]; !ok {
|
||||||
|
return nil, fmt.Errorf("missing required field 'type' in body")
|
||||||
|
}
|
||||||
|
if _, ok := data["service"]; !ok {
|
||||||
|
return nil, fmt.Errorf("missing required field 'service' in body")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if _, ok := data["upstreamPathPrefix"]; !ok {
|
||||||
|
// return nil, fmt.Errorf("missing required field 'upstreamPathPrefix' in body")
|
||||||
|
// }
|
||||||
|
|
||||||
|
_, ok = data["services"]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("missing required field 'port' in body")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Put("/v1/mcpServer", data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to add mcp server: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// add OpenAPI MCP tools to higress console, example request body:
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "id": null,
|
||||||
|
// "name": "openapi-name",
|
||||||
|
// "description": "123",
|
||||||
|
// "domains": [],
|
||||||
|
// "services": [
|
||||||
|
// {
|
||||||
|
// "name": "kubernetes.default.svc.cluster.local",
|
||||||
|
// "port": 443,
|
||||||
|
// "version": null,
|
||||||
|
// "weight": 100
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
// "type": "OPEN_API",
|
||||||
|
// "consumerAuthInfo": {
|
||||||
|
// "type": "key-auth",
|
||||||
|
// "enable": false,
|
||||||
|
// "allowedConsumers": []
|
||||||
|
// },
|
||||||
|
// "rawConfigurations": "", // MCP configuration str
|
||||||
|
// "dsn": null,
|
||||||
|
// "dbType": null,
|
||||||
|
// "upstreamPathPrefix": null,
|
||||||
|
// "mcpServerName": "openapi-name"
|
||||||
|
// }
|
||||||
|
func HandleAddOpenAPITool(client *HigressClient, body interface{}) ([]byte, error) {
|
||||||
|
return client.Put("/v1/mcpServer", body)
|
||||||
|
}
|
||||||
134
hgctl/pkg/agent/types.go
Normal file
134
hgctl/pkg/agent/types.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright (c) 2025 Alibaba Group Holding 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.
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Role string `json:"role"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
Messages []Message `json:"messages"`
|
||||||
|
FrequencyPenalty float64 `json:"frequency_penalty"`
|
||||||
|
PresencePenalty float64 `json:"presence_penalty"`
|
||||||
|
Stream bool `json:"stream"`
|
||||||
|
Temperature float64 `json:"temperature"`
|
||||||
|
Topp int32 `json:"top_p"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Choice struct {
|
||||||
|
Index int `json:"index"`
|
||||||
|
Message Message `json:"message"`
|
||||||
|
FinishReason string `json:"finish_reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Usage struct {
|
||||||
|
PromptTokens int `json:"prompt_tokens"`
|
||||||
|
CompletionTokens int `json:"completion_tokens"`
|
||||||
|
TotalTokens int `json:"total_tokens"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Choices []Choice `json:"choices"`
|
||||||
|
Created int64 `json:"created"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
Object string `json:"object"`
|
||||||
|
Usage Usage `json:"usage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToolsParam struct {
|
||||||
|
ToolName string `yaml:"toolName"`
|
||||||
|
Path string `yaml:"path"`
|
||||||
|
Method string `yaml:"method"`
|
||||||
|
ParamName []string `yaml:"paramName"`
|
||||||
|
Parameter string `yaml:"parameter"`
|
||||||
|
Description string `yaml:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Info struct {
|
||||||
|
Title string `yaml:"title"`
|
||||||
|
Description string `yaml:"description"`
|
||||||
|
Version string `yaml:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
URL string `yaml:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parameter struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
In string `yaml:"in"`
|
||||||
|
Description string `yaml:"description"`
|
||||||
|
Required bool `yaml:"required"`
|
||||||
|
Schema struct {
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
Default string `yaml:"default"`
|
||||||
|
Enum []string `yaml:"enum"`
|
||||||
|
} `yaml:"schema"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Items struct {
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
Example string `yaml:"example"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Property struct {
|
||||||
|
Description string `yaml:"description"`
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
Enum []string `yaml:"enum,omitempty"`
|
||||||
|
Items *Items `yaml:"items,omitempty"`
|
||||||
|
MaxItems int `yaml:"maxItems,omitempty"`
|
||||||
|
Example string `yaml:"example,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Schema struct {
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
Required []string `yaml:"required"`
|
||||||
|
Properties map[string]Property `yaml:"properties"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaType struct {
|
||||||
|
Schema Schema `yaml:"schema"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestBody struct {
|
||||||
|
Required bool `yaml:"required"`
|
||||||
|
Content map[string]MediaType `yaml:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathItem struct {
|
||||||
|
Description string `yaml:"description"`
|
||||||
|
Summary string `yaml:"summary"`
|
||||||
|
OperationID string `yaml:"operationId"`
|
||||||
|
RequestBody RequestBody `yaml:"requestBody"`
|
||||||
|
Parameters []Parameter `yaml:"parameters"`
|
||||||
|
Deprecated bool `yaml:"deprecated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Paths map[string]map[string]PathItem
|
||||||
|
|
||||||
|
type Components struct {
|
||||||
|
Schemas map[string]interface{} `yaml:"schemas"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type API struct {
|
||||||
|
OpenAPI string `yaml:"openapi"`
|
||||||
|
Info Info `yaml:"info"`
|
||||||
|
Servers []Server `yaml:"servers"`
|
||||||
|
Paths Paths `yaml:"paths"`
|
||||||
|
Components Components `yaml:"components"`
|
||||||
|
}
|
||||||
455
hgctl/pkg/agent/utils.go
Normal file
455
hgctl/pkg/agent/utils.go
Normal file
@@ -0,0 +1,455 @@
|
|||||||
|
// Copyright (c) 2025 Alibaba Group Holding 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.
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/braydonk/yaml"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/higress-group/openapi-to-mcpserver/pkg/converter"
|
||||||
|
"github.com/higress-group/openapi-to-mcpserver/pkg/models"
|
||||||
|
"github.com/higress-group/openapi-to-mcpserver/pkg/parser"
|
||||||
|
"github.com/manifoldco/promptui"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
k8s "k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
var binaryName = AgentBinaryName
|
||||||
|
|
||||||
|
// ------ cmd related ------
|
||||||
|
func BindFlagToEnv(cmd *cobra.Command, flagName, envName string) {
|
||||||
|
_ = viper.BindPFlag(flagName, cmd.PersistentFlags().Lookup(flagName))
|
||||||
|
_ = viper.BindEnv(flagName, envName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------ Prompt to install prequisite environment ------
|
||||||
|
func checkNodeInstall() bool {
|
||||||
|
cmd := exec.Command("node", "-v")
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
versionStr := strings.TrimPrefix(strings.TrimSpace(string(out)), "v")
|
||||||
|
parts := strings.Split(versionStr, ".")
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
major, err := strconv.Atoi(parts[0])
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return major >= NodeLeastVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func promptNodeInstall() error {
|
||||||
|
fmt.Println()
|
||||||
|
color.Yellow("⚠️ Node.js is not installed or not found in PATH.")
|
||||||
|
color.Cyan("🔧 Node.js is required to run the agent.")
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
options := []string{
|
||||||
|
"🚀 Install automatically (recommended)",
|
||||||
|
"📖 Exit and show manual installation guide",
|
||||||
|
}
|
||||||
|
|
||||||
|
var ans string
|
||||||
|
prompt := &survey.Select{
|
||||||
|
Message: "How would you like to install Node.js?",
|
||||||
|
Options: options,
|
||||||
|
}
|
||||||
|
if err := survey.AskOne(prompt, &ans); err != nil {
|
||||||
|
return fmt.Errorf("selection error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ans {
|
||||||
|
case "🚀 Install automatically (recommended)":
|
||||||
|
fmt.Println()
|
||||||
|
color.Green("🚀 Installing Node.js automatically...")
|
||||||
|
|
||||||
|
if err := installNodeAutomatically(); err != nil {
|
||||||
|
color.Red("❌ Installation failed: %v", err)
|
||||||
|
fmt.Println()
|
||||||
|
showNodeManualInstallation()
|
||||||
|
return errors.New("node.js installation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
color.Green("✅ Node.js installation completed!")
|
||||||
|
fmt.Println()
|
||||||
|
color.Blue("🔍 Verifying installation...")
|
||||||
|
|
||||||
|
if checkNodeInstall() {
|
||||||
|
color.Green("🎉 Node.js is now available!")
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
color.Yellow("⚠️ Node.js installation completed but not found in PATH.")
|
||||||
|
color.Cyan("💡 You may need to restart your terminal or source your shell profile.")
|
||||||
|
return errors.New("node.js installed but not in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
case "📖 Exit and show manual installation guide":
|
||||||
|
showNodeManualInstallation()
|
||||||
|
return errors.New("node.js not installed")
|
||||||
|
|
||||||
|
default:
|
||||||
|
return errors.New("invalid selection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func installNodeAutomatically() error {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
color.Cyan("📦 Please download Node.js installer from https://nodejs.org and run it manually on Windows")
|
||||||
|
return errors.New("automatic installation not supported on Windows yet")
|
||||||
|
case "darwin":
|
||||||
|
// macOS: use brew
|
||||||
|
cmd := exec.Command("brew", "install", "node")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
case "linux":
|
||||||
|
// Linux (Debian/Ubuntu example)
|
||||||
|
cmd := exec.Command("sudo", "apt", "update")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd = exec.Command("sudo", "apt", "install", "-y", "nodejs", "npm")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
default:
|
||||||
|
return errors.New("unsupported OS for automatic installation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func showNodeManualInstallation() {
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
color.New(color.FgGreen, color.Bold).Println("📖 Manual Node.js Installation Guide")
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
fmt.Println(color.MagentaString("Choose one of the following installation methods:"))
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
color.Cyan("Method 1: Install via package manager")
|
||||||
|
color.Cyan("macOS (brew): brew install node")
|
||||||
|
color.Cyan("Ubuntu/Debian: sudo apt install -y nodejs npm")
|
||||||
|
color.Cyan("Windows: download from https://nodejs.org and run installer")
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
color.Yellow("Method 2: Download from official website")
|
||||||
|
color.Yellow("1. Download Node.js from https://nodejs.org/en/download/")
|
||||||
|
color.Yellow("2. Follow installer instructions and add to PATH if needed")
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
color.Green("✅ Verify Installation")
|
||||||
|
fmt.Println(color.WhiteString("node -v"))
|
||||||
|
fmt.Println(color.WhiteString("npm -v"))
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
color.Cyan("💡 After installation, restart your terminal or source your shell profile.")
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAgentInstall() bool {
|
||||||
|
cmd := exec.Command(binaryName, "--version")
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func promptAgentInstall() error {
|
||||||
|
fmt.Println()
|
||||||
|
color.Yellow("⚠️ %s is not installed or not found in PATH.", binaryName)
|
||||||
|
color.Cyan("🔧 %s is required to run the agent.", binaryName)
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
options := []string{
|
||||||
|
"🚀 Install automatically (recommended)",
|
||||||
|
"📖 Exit and show manual installation guide",
|
||||||
|
}
|
||||||
|
|
||||||
|
var ans string
|
||||||
|
prompt := &survey.Select{
|
||||||
|
Message: "How would you like to install " + binaryName + "?",
|
||||||
|
Options: options,
|
||||||
|
}
|
||||||
|
if err := survey.AskOne(prompt, &ans); err != nil {
|
||||||
|
return fmt.Errorf("selection error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ans {
|
||||||
|
case "🚀 Install automatically (recommended)":
|
||||||
|
fmt.Println()
|
||||||
|
color.Green("🚀 Installing %s automatically...", binaryName)
|
||||||
|
|
||||||
|
if err := installAgentAutomatically(); err != nil {
|
||||||
|
color.Red("❌ Installation failed: %v", err)
|
||||||
|
fmt.Println()
|
||||||
|
showAgentManualInstallation()
|
||||||
|
return errors.New(binaryName + " installation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
color.Green("✅ %s installation completed!", binaryName)
|
||||||
|
fmt.Println()
|
||||||
|
color.Blue("🔍 Verifying installation...")
|
||||||
|
|
||||||
|
if checkAgentInstall() {
|
||||||
|
color.Green("🎉 %s is now available!", binaryName)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
color.Yellow("⚠️ %s installed but not found in PATH.", binaryName)
|
||||||
|
color.Cyan("💡 You may need to restart your terminal or source your shell profile.")
|
||||||
|
return errors.New(binaryName + " installed but not in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
case "📖 Exit and show manual installation guide":
|
||||||
|
showAgentManualInstallation()
|
||||||
|
return errors.New(binaryName + " not installed")
|
||||||
|
|
||||||
|
default:
|
||||||
|
return errors.New("invalid selection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func installAgentAutomatically() error {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
cmd := exec.Command("cmd", "/C", AgentInstallCmd)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
case "darwin":
|
||||||
|
cmd := exec.Command("bash", "-c", AgentInstallCmd)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
case "linux":
|
||||||
|
cmd := exec.Command("bash", "-c", AgentInstallCmd)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
default:
|
||||||
|
return errors.New("unsupported OS for automatic installation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func showAgentManualInstallation() {
|
||||||
|
fmt.Println()
|
||||||
|
color.New(color.FgGreen, color.Bold).Printf("📖 Manual %s Installation Guide\n", binaryName)
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
fmt.Println(color.MagentaString("Supported Operating Systems: macOS 10.15+, Ubuntu 20.04+/Debian 10+, or Windows 10+ (WSL/Git for Windows)"))
|
||||||
|
fmt.Println(color.MagentaString("Hardware: 4GB+ RAM"))
|
||||||
|
fmt.Println(color.MagentaString("Software: Node.js 18+"))
|
||||||
|
fmt.Println(color.MagentaString("Network: Internet connection required for authentication and AI processing"))
|
||||||
|
fmt.Println(color.MagentaString("Shell: Works best in Bash, Zsh, or Fish"))
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
color.Cyan("Method 1: Download prebuilt binary")
|
||||||
|
color.Cyan(fmt.Sprintf("1. Go to official release page: %s", AgentReleasePage))
|
||||||
|
fmt.Printf(color.CyanString("2. Download %s for your OS\n"), binaryName)
|
||||||
|
color.Cyan("3. Make it executable and place it in a directory in your PATH")
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
color.Green("✅ Verify Installation")
|
||||||
|
fmt.Printf(color.WhiteString("%s --version\n"), binaryName)
|
||||||
|
fmt.Println()
|
||||||
|
color.Cyan("💡 After installation, restart your terminal or source your shell profile.")
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------ MCP convert utils function ------
|
||||||
|
func parseOpenapi2MCP(arg MCPAddArg) *models.MCPConfig {
|
||||||
|
path := arg.spec
|
||||||
|
serverName := arg.name
|
||||||
|
|
||||||
|
// Create a new parser
|
||||||
|
p := parser.NewParser()
|
||||||
|
|
||||||
|
p.SetValidation(true)
|
||||||
|
|
||||||
|
// Parse the OpenAPI specification
|
||||||
|
err := p.ParseFile(path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error parsing OpenAPI specification: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := converter.NewConverter(p, models.ConvertOptions{
|
||||||
|
ServerName: serverName,
|
||||||
|
ToolNamePrefix: "",
|
||||||
|
TemplatePath: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Convert the OpenAPI specification to an MCP configuration
|
||||||
|
config, err := c.Convert()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error converting OpenAPI specification: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertMCPConfigToStr(cfg *models.MCPConfig) string {
|
||||||
|
var data []byte
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
encoder := yaml.NewEncoder(&buffer)
|
||||||
|
encoder.SetIndent(2)
|
||||||
|
|
||||||
|
if err := encoder.Encode(cfg); err != nil {
|
||||||
|
fmt.Printf("Error encoding YAML: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
data = buffer.Bytes()
|
||||||
|
str := string(data)
|
||||||
|
|
||||||
|
// fmt.Println("Successfully converted OpenAPI specification to MCP Server")
|
||||||
|
// fmt.Printf("Get MCP server config string: %v", str)
|
||||||
|
return str
|
||||||
|
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Printf("Error marshaling MCP configuration: %v\n", err)
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// err = os.WriteFile(*outputFile, data, 0644)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Printf("Error writing MCP configuration: %v\n", err)
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHigressGatewayServiceIP() (string, error) {
|
||||||
|
color.Cyan("🚀 Adding openapi MCP Server to agent, checking Higress Gateway Pod status...")
|
||||||
|
|
||||||
|
defaultKubeconfig := filepath.Join(os.Getenv("HOME"), ".kube", "config")
|
||||||
|
config, err := clientcmd.BuildConfigFromFlags("", defaultKubeconfig)
|
||||||
|
if err != nil {
|
||||||
|
color.Yellow("⚠️ Failed to load default kubeconfig: %v", err)
|
||||||
|
return promptForServiceKubeSettingsAndRetry()
|
||||||
|
}
|
||||||
|
|
||||||
|
clientset, err := k8s.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
color.Yellow("⚠️ Failed to create Kubernetes client: %v", err)
|
||||||
|
return promptForServiceKubeSettingsAndRetry()
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace := "higress-system"
|
||||||
|
svc, err := clientset.CoreV1().Services(namespace).Get(context.Background(), "higress-gateway", metav1.GetOptions{})
|
||||||
|
if err != nil || svc == nil {
|
||||||
|
color.Yellow("⚠️ Could not find Higress Gateway Service in namespace '%s'.", namespace)
|
||||||
|
return promptForServiceKubeSettingsAndRetry()
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := extractServiceIP(clientset, namespace, svc)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
color.Green("✅ Found Higress Gateway Service IP: %s (namespace: %s)", ip, namespace)
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// higress-gateway should always be LoadBalancer
|
||||||
|
func extractServiceIP(clientset *k8s.Clientset, namespace string, svc *v1.Service) (string, error) {
|
||||||
|
return svc.Spec.ClusterIP, nil
|
||||||
|
|
||||||
|
// // fallback to Pod IP
|
||||||
|
// if len(svc.Spec.Selector) > 0 {
|
||||||
|
// selector := metav1.FormatLabelSelector(&metav1.LabelSelector{MatchLabels: svc.Spec.Selector})
|
||||||
|
// pods, err := clientset.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{
|
||||||
|
// LabelSelector: selector,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// return "", fmt.Errorf("failed to list pods for selector: %v", err)
|
||||||
|
// }
|
||||||
|
// if len(pods.Items) > 0 {
|
||||||
|
// return pods.Items[0].Status.PodIP, nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// prompt fallback for user input
|
||||||
|
func promptForServiceKubeSettingsAndRetry() (string, error) {
|
||||||
|
color.Cyan("Let's fix it manually 👇")
|
||||||
|
|
||||||
|
kubeconfigPrompt := promptui.Prompt{
|
||||||
|
Label: "Enter kubeconfig path",
|
||||||
|
Default: filepath.Join(os.Getenv("HOME"), ".kube", "config"),
|
||||||
|
}
|
||||||
|
kubeconfigPath, err := kubeconfigPrompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("aborted: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nsPrompt := promptui.Prompt{
|
||||||
|
Label: "Enter Higress namespace",
|
||||||
|
Default: "higress-system",
|
||||||
|
}
|
||||||
|
namespace, err := nsPrompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to load kubeconfig: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientset, err := k8s.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create kubernetes client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
svc, err := clientset.CoreV1().Services(namespace).Get(context.Background(), "higress-gateway", metav1.GetOptions{})
|
||||||
|
if err != nil || svc == nil {
|
||||||
|
color.Red("❌ Higress Gateway Service not found in namespace '%s'", namespace)
|
||||||
|
return "", fmt.Errorf("service not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := extractServiceIP(clientset, namespace, svc)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
color.Green("✅ Found Higress Gateway Service IP: %s (namespace: %s)", ip, namespace)
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ package hgctl
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/hgctl/pkg/agent"
|
||||||
"github.com/alibaba/higress/hgctl/pkg/plugin"
|
"github.com/alibaba/higress/hgctl/pkg/plugin"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -42,6 +43,8 @@ func GetRootCommand() *cobra.Command {
|
|||||||
rootCmd.AddCommand(plugin.NewCommand())
|
rootCmd.AddCommand(plugin.NewCommand())
|
||||||
rootCmd.AddCommand(newCompletionCmd(os.Stdout))
|
rootCmd.AddCommand(newCompletionCmd(os.Stdout))
|
||||||
rootCmd.AddCommand(newCodeDebugCmd())
|
rootCmd.AddCommand(newCodeDebugCmd())
|
||||||
|
rootCmd.AddCommand(agent.NewMCPCmd())
|
||||||
|
rootCmd.AddCommand(agent.NewAgentCmd())
|
||||||
|
|
||||||
return rootCmd
|
return rootCmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ export VERSION
|
|||||||
HAS_CURL="$(type "curl" &>/dev/null && echo true || echo false)"
|
HAS_CURL="$(type "curl" &>/dev/null && echo true || echo false)"
|
||||||
HAS_WGET="$(type "wget" &>/dev/null && echo true || echo false)"
|
HAS_WGET="$(type "wget" &>/dev/null && echo true || echo false)"
|
||||||
HAS_GIT="$(type "git" &>/dev/null && echo true || echo false)"
|
HAS_GIT="$(type "git" &>/dev/null && echo true || echo false)"
|
||||||
|
HAS_NODE="$(type "node" &>/dev/null && echo true || echo false)"
|
||||||
|
|
||||||
|
# the lowest node version required
|
||||||
|
REQUIRED_NODE_VERSION="20.18.1"
|
||||||
|
|
||||||
# initArch discovers the architecture for this system.
|
# initArch discovers the architecture for this system.
|
||||||
initArch() {
|
initArch() {
|
||||||
@@ -76,8 +80,121 @@ verifySupported() {
|
|||||||
if [ "${HAS_GIT}" != "true" ]; then
|
if [ "${HAS_GIT}" != "true" ]; then
|
||||||
echo "[WARNING] Could not find git. It is required for plugin installation."
|
echo "[WARNING] Could not find git. It is required for plugin installation."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "${HAS_NODE}" != "true" ]; then
|
||||||
|
echo "[ERROR] Could not find node. It is required for hgctl agent support."
|
||||||
|
echo "Node.js >= ${REQUIRED_NODE_VERSION} is required."
|
||||||
|
echo "Start to install node..."
|
||||||
|
installNode
|
||||||
|
else
|
||||||
|
checkNodeVersion
|
||||||
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkNodeVersion() {
|
||||||
|
local current_version=$(node -v | sed 's/v//')
|
||||||
|
|
||||||
|
if ! verifyNodeVersion "$current_version" "$REQUIRED_NODE_VERSION"; then
|
||||||
|
echo "[ERROR] Node.js version $current_version is installed, but >= ${REQUIRED_NODE_VERSION} is required."
|
||||||
|
echo "Please upgrade Node.js or install a newer version."
|
||||||
|
echo "Visit: https://nodejs.org/ or use nvm: https://github.com/nvm-sh/nvm"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "[INFO] Node.js version $current_version meets the requirement (>= ${REQUIRED_NODE_VERSION})"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyNodeVersion() {
|
||||||
|
local current=$1
|
||||||
|
local required=$2
|
||||||
|
|
||||||
|
local current_major=$(echo "$current" | cut -d. -f1)
|
||||||
|
local current_minor=$(echo "$current" | cut -d. -f2)
|
||||||
|
local current_patch=$(echo "$current" | cut -d. -f3)
|
||||||
|
|
||||||
|
local required_major=$(echo "$required" | cut -d. -f1)
|
||||||
|
local required_minor=$(echo "$required" | cut -d. -f2)
|
||||||
|
local required_patch=$(echo "$required" | cut -d. -f3)
|
||||||
|
|
||||||
|
if [ "$current_major" -gt "$required_major" ]; then
|
||||||
|
return 0
|
||||||
|
elif [ "$current_major" -lt "$required_major" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$current_minor" -gt "$required_minor" ]; then
|
||||||
|
return 0
|
||||||
|
elif [ "$current_minor" -lt "$required_minor" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$current_patch" -ge "$required_patch" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
installNode() {
|
||||||
|
echo "Installing Node.js ${REQUIRED_NODE_VERSION}..."
|
||||||
|
|
||||||
|
case "$OS" in
|
||||||
|
darwin)
|
||||||
|
installNodeMacOS
|
||||||
|
;;
|
||||||
|
linux)
|
||||||
|
installNodeLinux
|
||||||
|
;;
|
||||||
|
windows)
|
||||||
|
installNodeWindows
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "[ERROR] Unsupported OS: $OS"
|
||||||
|
echo "Please install Node.js manually from https://nodejs.org/"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
installNodeMacOS() {
|
||||||
|
if type "brew" &>/dev/null; then
|
||||||
|
echo "Using Homebrew to install Node.js..."
|
||||||
|
brew install node@20
|
||||||
|
else
|
||||||
|
echo "[ERROR] Homebrew not found. Please install Homebrew first:"
|
||||||
|
echo " /bin/bash -c \\"\\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\\""
|
||||||
|
echo "Or install Node.js manually from https://nodejs.org/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
installNodeLinux() {
|
||||||
|
echo "Installing Node.js via NodeSource repository..."
|
||||||
|
|
||||||
|
if [ "${HAS_CURL}" == "true" ]; then
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||||
|
sudo apt-get install -y nodejs
|
||||||
|
elif [ "${HAS_WGET}" == "true" ]; then
|
||||||
|
wget -qO- https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||||
|
sudo apt-get install -y nodejs
|
||||||
|
else
|
||||||
|
echo "[ERROR] Neither curl nor wget found. Cannot install Node.js."
|
||||||
|
echo "Please install Node.js manually from https://nodejs.org/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
installNodeWindows() {
|
||||||
|
echo "[ERROR] Automatic Node.js installation on Windows is not supported."
|
||||||
|
echo "Please download and install Node.js manually from:"
|
||||||
|
echo " https://nodejs.org/dist/v${REQUIRED_NODE_VERSION}/node-v${REQUIRED_NODE_VERSION}-x64.msi"
|
||||||
|
echo "Or use a package manager like Chocolatey:"
|
||||||
|
echo " choco install nodejs --version=${REQUIRED_NODE_VERSION}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# checkDesiredVersion checks if the desired version is available.
|
# checkDesiredVersion checks if the desired version is available.
|
||||||
checkDesiredVersion() {
|
checkDesiredVersion() {
|
||||||
if [ "$VERSION" == "" ]; then
|
if [ "$VERSION" == "" ]; then
|
||||||
@@ -209,4 +326,4 @@ if ! checkhgctlInstalledVersion; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
testVersion
|
testVersion
|
||||||
cleanup
|
cleanup
|
||||||
Reference in New Issue
Block a user