mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-06-22 09:51:56 +08:00
Compare commits
8 Commits
tools/goct
...
f6ecaa2015
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6ecaa2015 | ||
|
|
52d2bdadcd | ||
|
|
3738be1945 | ||
|
|
5b74b9ab7b | ||
|
|
4a67261b7b | ||
|
|
22bdae0787 | ||
|
|
e8675d6a9a | ||
|
|
e441c44975 |
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
|||||||
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
|
||||||
- name: Codecov
|
- name: Codecov
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v6
|
||||||
with:
|
with:
|
||||||
files: ./coverage.txt
|
files: ./coverage.txt
|
||||||
flags: unittests
|
flags: unittests
|
||||||
|
|||||||
50
go.mod
50
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/zeromicro/go-zero
|
module github.com/zeromicro/go-zero
|
||||||
|
|
||||||
go 1.24.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||||
@@ -12,9 +12,9 @@ require (
|
|||||||
github.com/golang/protobuf v1.5.4
|
github.com/golang/protobuf v1.5.4
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/grafana/pyroscope-go v1.2.8
|
github.com/grafana/pyroscope-go v1.2.8
|
||||||
github.com/jackc/pgx/v5 v5.8.0
|
github.com/jackc/pgx/v5 v5.9.2
|
||||||
github.com/jhump/protoreflect v1.18.0
|
github.com/jhump/protoreflect v1.18.0
|
||||||
github.com/modelcontextprotocol/go-sdk v1.4.0
|
github.com/modelcontextprotocol/go-sdk v1.4.1
|
||||||
github.com/pelletier/go-toml/v2 v2.3.0
|
github.com/pelletier/go-toml/v2 v2.3.0
|
||||||
github.com/prometheus/client_golang v1.23.2
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/redis/go-redis/v9 v9.18.0
|
github.com/redis/go-redis/v9 v9.18.0
|
||||||
@@ -23,22 +23,22 @@ require (
|
|||||||
github.com/titanous/json5 v1.0.0
|
github.com/titanous/json5 v1.0.0
|
||||||
go.etcd.io/etcd/api/v3 v3.5.21
|
go.etcd.io/etcd/api/v3 v3.5.21
|
||||||
go.etcd.io/etcd/client/v3 v3.5.21
|
go.etcd.io/etcd/client/v3 v3.5.21
|
||||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
go.mongodb.org/mongo-driver/v2 v2.6.0
|
||||||
go.opentelemetry.io/otel v1.40.0
|
go.opentelemetry.io/otel v1.43.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.40.0
|
go.opentelemetry.io/otel/exporters/zipkin v1.40.0
|
||||||
go.opentelemetry.io/otel/sdk v1.40.0
|
go.opentelemetry.io/otel/sdk v1.43.0
|
||||||
go.opentelemetry.io/otel/trace v1.40.0
|
go.opentelemetry.io/otel/trace v1.43.0
|
||||||
go.uber.org/automaxprocs v1.6.0
|
go.uber.org/automaxprocs v1.6.0
|
||||||
go.uber.org/goleak v1.3.0
|
go.uber.org/goleak v1.3.0
|
||||||
go.uber.org/mock v0.6.0
|
go.uber.org/mock v0.6.0
|
||||||
golang.org/x/net v0.50.0
|
golang.org/x/net v0.52.0
|
||||||
golang.org/x/sys v0.41.0
|
golang.org/x/sys v0.42.0
|
||||||
golang.org/x/time v0.14.0
|
golang.org/x/time v0.14.0
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409
|
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9
|
||||||
google.golang.org/grpc v1.79.3
|
google.golang.org/grpc v1.80.0
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||||
gopkg.in/h2non/gock.v1 v1.1.2
|
gopkg.in/h2non/gock.v1 v1.1.2
|
||||||
@@ -50,7 +50,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
@@ -63,7 +63,7 @@ require (
|
|||||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
|
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
|
||||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
|
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // 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.21.0 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
@@ -74,7 +74,7 @@ require (
|
|||||||
github.com/google/go-cmp v0.7.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/jsonschema-go v0.4.2 // indirect
|
github.com/google/jsonschema-go v0.4.2 // indirect
|
||||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
|
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
@@ -101,7 +101,7 @@ require (
|
|||||||
github.com/prometheus/procfs v0.16.1 // indirect
|
github.com/prometheus/procfs v0.16.1 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/segmentio/asm v1.1.3 // indirect
|
github.com/segmentio/asm v1.1.3 // indirect
|
||||||
github.com/segmentio/encoding v0.5.3 // indirect
|
github.com/segmentio/encoding v0.5.4 // indirect
|
||||||
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
|
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
@@ -113,20 +113,20 @@ require (
|
|||||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect
|
go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||||
go.uber.org/atomic v1.11.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
go.uber.org/zap v1.24.0 // indirect
|
go.uber.org/zap v1.24.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/oauth2 v0.34.0 // indirect
|
golang.org/x/oauth2 v0.35.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/term v0.40.0 // indirect
|
golang.org/x/term v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|||||||
108
go.sum
108
go.sum
@@ -1,5 +1,5 @@
|
|||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||||
github.com/alicebob/miniredis/v2 v2.37.0 h1:RheObYW32G1aiJIj81XVt78ZHJpHonHLHW7OLIshq68=
|
github.com/alicebob/miniredis/v2 v2.37.0 h1:RheObYW32G1aiJIj81XVt78ZHJpHonHLHW7OLIshq68=
|
||||||
@@ -42,8 +42,8 @@ github.com/fullstorydev/grpcurl v1.9.3 h1:PC1Xi3w+JAvEE2Tg2Gf2RfVgPbf9+tbuQr1Zky
|
|||||||
github.com/fullstorydev/grpcurl v1.9.3/go.mod h1:/b4Wxe8bG6ndAjlfSUjwseQReUDUvBJiFEB7UllOlUE=
|
github.com/fullstorydev/grpcurl v1.9.3/go.mod h1:/b4Wxe8bG6ndAjlfSUjwseQReUDUvBJiFEB7UllOlUE=
|
||||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
@@ -86,16 +86,16 @@ github.com/grafana/pyroscope-go v1.2.8 h1:UvCwIhlx9DeV7F6TW/z8q1Mi4PIm3vuUJ2ZlCE
|
|||||||
github.com/grafana/pyroscope-go v1.2.8/go.mod h1:SSi59eQ1/zmKoY/BKwa5rSFsJaq+242Bcrr4wPix1g8=
|
github.com/grafana/pyroscope-go v1.2.8/go.mod h1:SSi59eQ1/zmKoY/BKwa5rSFsJaq+242Bcrr4wPix1g8=
|
||||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og=
|
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og=
|
||||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
|
github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
|
github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
|
||||||
github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
|
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/jhump/protoreflect v1.18.0 h1:TOz0MSR/0JOZ5kECB/0ufGnC2jdsgZ123Rd/k4Z5/2w=
|
github.com/jhump/protoreflect v1.18.0 h1:TOz0MSR/0JOZ5kECB/0ufGnC2jdsgZ123Rd/k4Z5/2w=
|
||||||
@@ -131,8 +131,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/modelcontextprotocol/go-sdk v1.4.0 h1:u0kr8lbJc1oBcawK7Df+/ajNMpIDFE41OEPxdeTLOn8=
|
github.com/modelcontextprotocol/go-sdk v1.4.1 h1:M4x9GyIPj+HoIlHNGpK2hq5o3BFhC+78PkEaldQRphc=
|
||||||
github.com/modelcontextprotocol/go-sdk v1.4.0/go.mod h1:Nxc2n+n/GdCebUaqCOhTetptS17SXXNu9IfNTaLDi1E=
|
github.com/modelcontextprotocol/go-sdk v1.4.1/go.mod h1:Bo/mS87hPQqHSRkMv4dQq1XCu6zv4INdXnFZabkNU6s=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -179,8 +179,8 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
|
|||||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc=
|
github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc=
|
||||||
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
|
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
|
||||||
github.com/segmentio/encoding v0.5.3 h1:OjMgICtcSFuNvQCdwqMCv9Tg7lEOXGwm1J5RPQccx6w=
|
github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0=
|
||||||
github.com/segmentio/encoding v0.5.3/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
|
github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
@@ -226,32 +226,32 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.21 h1:lPBu71Y7osQmzlflM9OfeIV2JlmpBjqBNlLtcoB
|
|||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.21/go.mod h1:BgqT/IXPjK9NkeSDjbzwsHySX3yIle2+ndz28nVsjUs=
|
go.etcd.io/etcd/client/pkg/v3 v3.5.21/go.mod h1:BgqT/IXPjK9NkeSDjbzwsHySX3yIle2+ndz28nVsjUs=
|
||||||
go.etcd.io/etcd/client/v3 v3.5.21 h1:T6b1Ow6fNjOLOtM0xSoKNQt1ASPCLWrF9XMHcH9pEyY=
|
go.etcd.io/etcd/client/v3 v3.5.21 h1:T6b1Ow6fNjOLOtM0xSoKNQt1ASPCLWrF9XMHcH9pEyY=
|
||||||
go.etcd.io/etcd/client/v3 v3.5.21/go.mod h1:mFYy67IOqmbRf/kRUvsHixzo3iG+1OF2W2+jVIQRAnU=
|
go.etcd.io/etcd/client/v3 v3.5.21/go.mod h1:mFYy67IOqmbRf/kRUvsHixzo3iG+1OF2W2+jVIQRAnU=
|
||||||
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8=
|
||||||
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.40.0 h1:zu+I4j+FdO6xIxBVPeuncQVbjxUM4LiMgv6GwGe9REE=
|
go.opentelemetry.io/otel/exporters/zipkin v1.40.0 h1:zu+I4j+FdO6xIxBVPeuncQVbjxUM4LiMgv6GwGe9REE=
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.40.0/go.mod h1:zS6cC4nFBYXbu18e7aLfMzubBjOiN7ZcROu477qtMf8=
|
go.opentelemetry.io/otel/exporters/zipkin v1.40.0/go.mod h1:zS6cC4nFBYXbu18e7aLfMzubBjOiN7ZcROu477qtMf8=
|
||||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||||
@@ -272,8 +272,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
@@ -283,16 +283,16 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
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=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -302,18 +302,18 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -321,20 +321,20 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
33
mcp/options.go
Normal file
33
mcp/options.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package mcp
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// RequestMetadataExtractor extracts request metadata for downstream handlers.
|
||||||
|
type RequestMetadataExtractor func(*http.Request) RequestMetadata
|
||||||
|
|
||||||
|
// McpOption customizes MCP server construction.
|
||||||
|
type McpOption interface {
|
||||||
|
apply(*serverOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
type mcpOptionFunc func(*serverOptions)
|
||||||
|
|
||||||
|
func (f mcpOptionFunc) apply(opts *serverOptions) {
|
||||||
|
f(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverOptions struct {
|
||||||
|
requestMetadataExtractor RequestMetadataExtractor
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultServerOptions() serverOptions {
|
||||||
|
return serverOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRequestMetadataExtractor installs an extractor that runs for each incoming
|
||||||
|
// MCP HTTP request, and stores the extracted metadata into handler context.
|
||||||
|
func WithRequestMetadataExtractor(extractor RequestMetadataExtractor) McpOption {
|
||||||
|
return mcpOptionFunc(func(opts *serverOptions) {
|
||||||
|
opts.requestMetadataExtractor = extractor
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ This package provides a go-zero integration for the [Model Context Protocol (MCP
|
|||||||
- **CORS Support**: Configurable CORS settings for cross-origin requests
|
- **CORS Support**: Configurable CORS settings for cross-origin requests
|
||||||
- **Type-Safe Tool Handlers**: Generic tool handlers with automatic JSON schema generation
|
- **Type-Safe Tool Handlers**: Generic tool handlers with automatic JSON schema generation
|
||||||
- **Prompts and Resources**: Full support for MCP prompts and resources
|
- **Prompts and Resources**: Full support for MCP prompts and resources
|
||||||
|
- **Request Metadata Bridge**: Optional request metadata extraction into handler context
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@@ -220,6 +221,35 @@ mcp:
|
|||||||
messageEndpoint: /message
|
messageEndpoint: /message
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Request Metadata Bridge
|
||||||
|
|
||||||
|
For multi-tenant or request-context-aware tools, you can extract selected HTTP request metadata once at the transport boundary and read it from `context.Context` in handlers.
|
||||||
|
|
||||||
|
```go
|
||||||
|
server := mcp.NewMcpServerWithOptions(c,
|
||||||
|
mcp.WithRequestMetadataExtractor(mcp.DefaultRequestMetadataExtractor),
|
||||||
|
)
|
||||||
|
|
||||||
|
handler := func(ctx context.Context, req *mcp.CallToolRequest, args SomeArgs) (*mcp.CallToolResult, any, error) {
|
||||||
|
tenant, _ := mcp.HeaderFromContext(ctx, "X-Tenant-Id")
|
||||||
|
traceID, _ := mcp.QueryFromContext(ctx, "trace")
|
||||||
|
scope, _ := mcp.PathFromContext(ctx, "scope")
|
||||||
|
|
||||||
|
_ = tenant
|
||||||
|
_ = traceID
|
||||||
|
_ = scope
|
||||||
|
|
||||||
|
return &mcp.CallToolResult{}, nil, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Available helpers:
|
||||||
|
|
||||||
|
- `RequestMetadataFromContext(ctx)`
|
||||||
|
- `HeaderFromContext(ctx, key)`
|
||||||
|
- `QueryFromContext(ctx, key)`
|
||||||
|
- `PathFromContext(ctx, key)`
|
||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
| Field | Type | Default | Description |
|
| Field | Type | Default | Description |
|
||||||
|
|||||||
150
mcp/request_metadata.go
Normal file
150
mcp/request_metadata.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
package mcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequestMetadata carries selected request-scoped values into MCP handlers.
|
||||||
|
type RequestMetadata struct {
|
||||||
|
Headers map[string][]string
|
||||||
|
Query map[string][]string
|
||||||
|
Path map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type requestMetadataCtxKey struct{}
|
||||||
|
|
||||||
|
// RequestMetadataFromContext returns metadata extracted at the transport boundary.
|
||||||
|
func RequestMetadataFromContext(ctx context.Context) (RequestMetadata, bool) {
|
||||||
|
metadata, ok := requestMetadataFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return RequestMetadata{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeRequestMetadata(metadata), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderFromContext returns the first header value for key.
|
||||||
|
func HeaderFromContext(ctx context.Context, key string) (string, bool) {
|
||||||
|
metadata, ok := requestMetadataFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := metadata.Headers[http.CanonicalHeaderKey(key)]
|
||||||
|
if len(vals) == 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return vals[0], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryFromContext returns the first query value for key.
|
||||||
|
func QueryFromContext(ctx context.Context, key string) (string, bool) {
|
||||||
|
metadata, ok := requestMetadataFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := metadata.Query[key]
|
||||||
|
if len(vals) == 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return vals[0], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathFromContext returns the path variable value for key.
|
||||||
|
func PathFromContext(ctx context.Context, key string) (string, bool) {
|
||||||
|
metadata, ok := requestMetadataFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
val, ok := metadata.Path[key]
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestMetadataFromContext(ctx context.Context) (RequestMetadata, bool) {
|
||||||
|
metadata, ok := ctx.Value(requestMetadataCtxKey{}).(RequestMetadata)
|
||||||
|
if !ok {
|
||||||
|
return RequestMetadata{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultRequestMetadataExtractor extracts headers, query values, and path variables.
|
||||||
|
func DefaultRequestMetadataExtractor(r *http.Request) RequestMetadata {
|
||||||
|
metadata := RequestMetadata{
|
||||||
|
Headers: make(map[string][]string, len(r.Header)),
|
||||||
|
Query: make(map[string][]string),
|
||||||
|
Path: clonePathVars(pathvar.Vars(r)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, vals := range r.Header {
|
||||||
|
metadata.Headers[http.CanonicalHeaderKey(key)] = append([]string(nil), vals...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.URL != nil {
|
||||||
|
for key, vals := range r.URL.Query() {
|
||||||
|
metadata.Query[key] = append([]string(nil), vals...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeRequestMetadata(metadata RequestMetadata) RequestMetadata {
|
||||||
|
return RequestMetadata{
|
||||||
|
Headers: cloneCanonicalHeaderValues(metadata.Headers),
|
||||||
|
Query: cloneHeaderValues(metadata.Query),
|
||||||
|
Path: clonePathVars(metadata.Path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneHeaderValues(values map[string][]string) map[string][]string {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cloned := make(map[string][]string, len(values))
|
||||||
|
for key, vals := range values {
|
||||||
|
cloned[key] = append([]string(nil), vals...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneCanonicalHeaderValues(values map[string][]string) map[string][]string {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cloned := make(map[string][]string, len(values))
|
||||||
|
for key, vals := range values {
|
||||||
|
canonical := http.CanonicalHeaderKey(key)
|
||||||
|
cloned[canonical] = append(cloned[canonical], vals...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
|
|
||||||
|
func clonePathVars(values map[string]string) map[string]string {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cloned := make(map[string]string, len(values))
|
||||||
|
for key, val := range values {
|
||||||
|
cloned[key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
185
mcp/request_metadata_test.go
Normal file
185
mcp/request_metadata_test.go
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
package mcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDefaultRequestMetadataExtractor(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/sse?tenant=t1&trace=abc", nil)
|
||||||
|
req.Header.Add("X-Tenant-Id", "tenant-from-header")
|
||||||
|
req = pathvar.WithVars(req, map[string]string{"tool": "sum"})
|
||||||
|
|
||||||
|
metadata := DefaultRequestMetadataExtractor(req)
|
||||||
|
header, ok := metadata.Headers["X-Tenant-Id"]
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, []string{"tenant-from-header"}, header)
|
||||||
|
assert.Equal(t, []string{"t1"}, metadata.Query["tenant"])
|
||||||
|
assert.Equal(t, "sum", metadata.Path["tool"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestMetadataContextHelpers(t *testing.T) {
|
||||||
|
ctx := context.WithValue(context.Background(), requestMetadataCtxKey{}, RequestMetadata{
|
||||||
|
Headers: map[string][]string{"X-Trace-Id": {"trace-1"}},
|
||||||
|
Query: map[string][]string{"tenant": {"foo"}},
|
||||||
|
Path: map[string]string{"scope": "prod"},
|
||||||
|
})
|
||||||
|
|
||||||
|
metadata, ok := RequestMetadataFromContext(ctx)
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, []string{"trace-1"}, metadata.Headers["X-Trace-Id"])
|
||||||
|
|
||||||
|
header, ok := HeaderFromContext(ctx, "x-trace-id")
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, "trace-1", header)
|
||||||
|
|
||||||
|
query, ok := QueryFromContext(ctx, "tenant")
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, "foo", query)
|
||||||
|
|
||||||
|
path, ok := PathFromContext(ctx, "scope")
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, "prod", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestMetadataContextHelpersMissingKeys(t *testing.T) {
|
||||||
|
ctx := context.WithValue(context.Background(), requestMetadataCtxKey{}, RequestMetadata{
|
||||||
|
Headers: map[string][]string{"X-Trace-Id": {"trace-1"}},
|
||||||
|
Query: map[string][]string{"tenant": {"foo"}},
|
||||||
|
Path: map[string]string{"scope": "prod"},
|
||||||
|
})
|
||||||
|
|
||||||
|
_, ok := HeaderFromContext(ctx, "x-missing")
|
||||||
|
assert.False(t, ok)
|
||||||
|
|
||||||
|
_, ok = QueryFromContext(ctx, "missing")
|
||||||
|
assert.False(t, ok)
|
||||||
|
|
||||||
|
_, ok = PathFromContext(ctx, "missing")
|
||||||
|
assert.False(t, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestMetadataFromContextNotFound(t *testing.T) {
|
||||||
|
_, ok := RequestMetadataFromContext(context.Background())
|
||||||
|
assert.False(t, ok)
|
||||||
|
|
||||||
|
_, ok = HeaderFromContext(context.Background(), "x-test")
|
||||||
|
assert.False(t, ok)
|
||||||
|
|
||||||
|
_, ok = QueryFromContext(context.Background(), "tenant")
|
||||||
|
assert.False(t, ok)
|
||||||
|
|
||||||
|
_, ok = PathFromContext(context.Background(), "tenant")
|
||||||
|
assert.False(t, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrapRequestMetadata(t *testing.T) {
|
||||||
|
s := &mcpServerImpl{
|
||||||
|
options: serverOptions{
|
||||||
|
requestMetadataExtractor: DefaultRequestMetadataExtractor,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
called := false
|
||||||
|
handler := s.wrapRequestMetadata(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||||
|
called = true
|
||||||
|
header, ok := HeaderFromContext(r.Context(), "x-tenant-id")
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, "tenant-1", header)
|
||||||
|
|
||||||
|
query, ok := QueryFromContext(r.Context(), "tenant")
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, "q-tenant", query)
|
||||||
|
}))
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/sse?tenant=q-tenant", nil)
|
||||||
|
req.Header.Set("X-Tenant-Id", "tenant-1")
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
assert.True(t, called)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrapRequestMetadataNoExtractor(t *testing.T) {
|
||||||
|
s := &mcpServerImpl{}
|
||||||
|
|
||||||
|
called := false
|
||||||
|
handler := s.wrapRequestMetadata(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||||
|
called = true
|
||||||
|
_, ok := RequestMetadataFromContext(r.Context())
|
||||||
|
assert.False(t, ok)
|
||||||
|
}))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/sse", nil))
|
||||||
|
|
||||||
|
assert.True(t, called)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrapRequestMetadataCanonicalizesCustomHeaders(t *testing.T) {
|
||||||
|
s := &mcpServerImpl{
|
||||||
|
options: serverOptions{
|
||||||
|
requestMetadataExtractor: func(*http.Request) RequestMetadata {
|
||||||
|
return RequestMetadata{
|
||||||
|
Headers: map[string][]string{
|
||||||
|
"x-tenant-id": {"tenant-lower"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
called := false
|
||||||
|
handler := s.wrapRequestMetadata(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||||
|
called = true
|
||||||
|
header, ok := HeaderFromContext(r.Context(), "X-Tenant-Id")
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, "tenant-lower", header)
|
||||||
|
}))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/sse", nil))
|
||||||
|
|
||||||
|
assert.True(t, called)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestMetadataFromContextReturnsCopy(t *testing.T) {
|
||||||
|
ctx := context.WithValue(context.Background(), requestMetadataCtxKey{}, RequestMetadata{
|
||||||
|
Headers: map[string][]string{"X-Trace-Id": {"trace-1"}},
|
||||||
|
})
|
||||||
|
|
||||||
|
metadata, ok := RequestMetadataFromContext(ctx)
|
||||||
|
assert.True(t, ok)
|
||||||
|
metadata.Headers["X-Trace-Id"][0] = "mutated"
|
||||||
|
metadata.Headers["X-New"] = []string{"new"}
|
||||||
|
|
||||||
|
fresh, ok := RequestMetadataFromContext(ctx)
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, []string{"trace-1"}, fresh.Headers["X-Trace-Id"])
|
||||||
|
assert.Nil(t, fresh.Headers["X-New"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestMetadataFromContextWithEmptyAndCanonicalizedHeaders(t *testing.T) {
|
||||||
|
emptyCtx := context.WithValue(context.Background(), requestMetadataCtxKey{}, RequestMetadata{})
|
||||||
|
empty, ok := RequestMetadataFromContext(emptyCtx)
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Nil(t, empty.Headers)
|
||||||
|
assert.Nil(t, empty.Query)
|
||||||
|
assert.Nil(t, empty.Path)
|
||||||
|
|
||||||
|
ctx := context.WithValue(context.Background(), requestMetadataCtxKey{}, RequestMetadata{
|
||||||
|
Headers: map[string][]string{
|
||||||
|
"x-tenant-id": {"a"},
|
||||||
|
"X-Tenant-Id": {"b"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
metadata, ok := RequestMetadataFromContext(ctx)
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, []string{"a", "b"}, metadata.Headers["X-Tenant-Id"])
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package mcp
|
package mcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
|
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
|
||||||
@@ -20,10 +21,23 @@ type mcpServerImpl struct {
|
|||||||
conf McpConf
|
conf McpConf
|
||||||
httpServer *rest.Server
|
httpServer *rest.Server
|
||||||
mcpServer *sdkmcp.Server
|
mcpServer *sdkmcp.Server
|
||||||
|
options serverOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMcpServer creates a new MCP server using the official SDK
|
// NewMcpServer creates a new MCP server using the official SDK
|
||||||
func NewMcpServer(c McpConf) McpServer {
|
func NewMcpServer(c McpConf) McpServer {
|
||||||
|
return NewMcpServerWithOptions(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMcpServerWithOptions creates a new MCP server with optional customizations.
|
||||||
|
func NewMcpServerWithOptions(c McpConf, opts ...McpOption) McpServer {
|
||||||
|
serverOpts := defaultServerOptions()
|
||||||
|
for _, opt := range opts {
|
||||||
|
if opt != nil {
|
||||||
|
opt.apply(&serverOpts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create the underlying rest HTTP server
|
// Create the underlying rest HTTP server
|
||||||
var httpServer *rest.Server
|
var httpServer *rest.Server
|
||||||
if len(c.Mcp.Cors) == 0 {
|
if len(c.Mcp.Cors) == 0 {
|
||||||
@@ -52,6 +66,7 @@ func NewMcpServer(c McpConf) McpServer {
|
|||||||
conf: c,
|
conf: c,
|
||||||
httpServer: httpServer,
|
httpServer: httpServer,
|
||||||
mcpServer: mcpServer,
|
mcpServer: mcpServer,
|
||||||
|
options: serverOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose transport based on configuration
|
// Choose transport based on configuration
|
||||||
@@ -85,7 +100,7 @@ func (s *mcpServerImpl) setupSSETransport() {
|
|||||||
return s.mcpServer
|
return s.mcpServer
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
s.registerRoutes(handler, s.conf.Mcp.SseEndpoint)
|
s.registerRoutes(s.wrapRequestMetadata(handler), s.conf.Mcp.SseEndpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupStreamableTransport configures the server to use Streamable HTTP transport (2025-03-26 spec)
|
// setupStreamableTransport configures the server to use Streamable HTTP transport (2025-03-26 spec)
|
||||||
@@ -96,7 +111,7 @@ func (s *mcpServerImpl) setupStreamableTransport() {
|
|||||||
return s.mcpServer
|
return s.mcpServer
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
s.registerRoutes(handler, s.conf.Mcp.MessageEndpoint)
|
s.registerRoutes(s.wrapRequestMetadata(handler), s.conf.Mcp.MessageEndpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *mcpServerImpl) registerRoutes(handler http.Handler, endpoint string) {
|
func (s *mcpServerImpl) registerRoutes(handler http.Handler, endpoint string) {
|
||||||
@@ -113,3 +128,16 @@ func (s *mcpServerImpl) registerRoutes(handler http.Handler, endpoint string) {
|
|||||||
Handler: handler.ServeHTTP,
|
Handler: handler.ServeHTTP,
|
||||||
}, rest.WithTimeout(s.conf.Mcp.MessageTimeout))
|
}, rest.WithTimeout(s.conf.Mcp.MessageTimeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *mcpServerImpl) wrapRequestMetadata(next http.Handler) http.Handler {
|
||||||
|
extractor := s.options.requestMetadataExtractor
|
||||||
|
if extractor == nil {
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
metadata := normalizeRequestMetadata(extractor(r))
|
||||||
|
ctx := context.WithValue(r.Context(), requestMetadataCtxKey{}, metadata)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ package mcp
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/zeromicro/go-zero/core/conf"
|
"github.com/zeromicro/go-zero/core/conf"
|
||||||
)
|
)
|
||||||
@@ -391,3 +394,148 @@ func TestAddToolWithCustomServer(t *testing.T) {
|
|||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRequestMetadataIntegrationSSEToolCall(t *testing.T) {
|
||||||
|
port := getFreePort(t)
|
||||||
|
|
||||||
|
c := McpConf{}
|
||||||
|
c.Host = "127.0.0.1"
|
||||||
|
c.Port = port
|
||||||
|
c.Mcp.Name = "metadata-integration-test"
|
||||||
|
c.Mcp.UseStreamable = false
|
||||||
|
c.Mcp.SseEndpoint = "/sse/:scope"
|
||||||
|
c.Mcp.MessageTimeout = 2 * time.Second
|
||||||
|
c.Mcp.SseTimeout = 2 * time.Second
|
||||||
|
|
||||||
|
server := NewMcpServerWithOptions(c, WithRequestMetadataExtractor(DefaultRequestMetadataExtractor))
|
||||||
|
|
||||||
|
tool := &Tool{
|
||||||
|
Name: "inspect_metadata",
|
||||||
|
Description: "Inspect metadata in handler context",
|
||||||
|
}
|
||||||
|
|
||||||
|
type Args struct{}
|
||||||
|
|
||||||
|
AddTool(server, tool, func(ctx context.Context, req *CallToolRequest, args Args) (*CallToolResult, any, error) {
|
||||||
|
header, ok := HeaderFromContext(ctx, "x-tenant-id")
|
||||||
|
if !ok || header != "tenant-header" {
|
||||||
|
return nil, nil, fmt.Errorf("unexpected header from context: %q", header)
|
||||||
|
}
|
||||||
|
|
||||||
|
query, ok := QueryFromContext(ctx, "tenant")
|
||||||
|
if !ok || query != "tenant-query" {
|
||||||
|
return nil, nil, fmt.Errorf("unexpected query from context: %q", query)
|
||||||
|
}
|
||||||
|
|
||||||
|
scope, ok := PathFromContext(ctx, "scope")
|
||||||
|
if !ok || scope != "prod" {
|
||||||
|
return nil, nil, fmt.Errorf("unexpected path from context: %q", scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CallToolResult{
|
||||||
|
Content: []Content{&TextContent{Text: "metadata-ok"}},
|
||||||
|
}, nil, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
go server.Start()
|
||||||
|
t.Cleanup(server.Stop)
|
||||||
|
|
||||||
|
baseURL := fmt.Sprintf("http://127.0.0.1:%d/sse/prod?tenant=tenant-query", port)
|
||||||
|
waitForServerReady(t, baseURL, 2*time.Second)
|
||||||
|
|
||||||
|
client := sdkmcp.NewClient(&sdkmcp.Implementation{
|
||||||
|
Name: "metadata-client",
|
||||||
|
Version: "1.0.0",
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Timeout: 2 * time.Second,
|
||||||
|
Transport: metadataHeaderRoundTripper{
|
||||||
|
next: http.DefaultTransport,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
transport := &sdkmcp.SSEClientTransport{
|
||||||
|
Endpoint: baseURL,
|
||||||
|
HTTPClient: httpClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := client.Connect(context.Background(), transport, nil)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = session.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
res, err := session.CallTool(context.Background(), &sdkmcp.CallToolParams{
|
||||||
|
Name: "inspect_metadata",
|
||||||
|
Arguments: map[string]any{},
|
||||||
|
})
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assert.NotNil(t, res) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.False(t, res.IsError)
|
||||||
|
}
|
||||||
|
|
||||||
|
type metadataHeaderRoundTripper struct {
|
||||||
|
next http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r metadataHeaderRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
next := r.next
|
||||||
|
if next == nil {
|
||||||
|
next = http.DefaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
clone := req.Clone(req.Context())
|
||||||
|
clone.Header.Set("X-Tenant-Id", "tenant-header")
|
||||||
|
return next.RoundTrip(clone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFreePort(t *testing.T) int {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
addr, ok := listener.Addr().(*net.TCPAddr)
|
||||||
|
if !assert.True(t, ok) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForServerReady(t *testing.T, endpoint string, timeout time.Duration) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 200 * time.Millisecond}
|
||||||
|
deadline := time.Now().Add(timeout)
|
||||||
|
for time.Now().Before(deadline) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to build readiness request: %v", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Accept", "text/event-stream")
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err == nil {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
if resp.StatusCode > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatalf("server did not become ready for %s within %s", endpoint, timeout)
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ require (
|
|||||||
github.com/emicklei/proto v1.14.3
|
github.com/emicklei/proto v1.14.3
|
||||||
github.com/fatih/structtag v1.2.0
|
github.com/fatih/structtag v1.2.0
|
||||||
github.com/go-openapi/spec v0.21.1-0.20250328170532-a3928469592e
|
github.com/go-openapi/spec v0.21.1-0.20250328170532-a3928469592e
|
||||||
github.com/go-sql-driver/mysql v1.9.3
|
github.com/go-sql-driver/mysql v1.10.0
|
||||||
github.com/gookit/color v1.6.0
|
github.com/gookit/color v1.6.0
|
||||||
github.com/iancoleman/strcase v0.3.0
|
github.com/iancoleman/strcase v0.3.0
|
||||||
github.com/spf13/cobra v1.10.2
|
github.com/spf13/cobra v1.10.2
|
||||||
@@ -18,13 +18,13 @@ require (
|
|||||||
github.com/zeromicro/ddl-parser v1.0.5
|
github.com/zeromicro/ddl-parser v1.0.5
|
||||||
github.com/zeromicro/go-zero v1.10.1
|
github.com/zeromicro/go-zero v1.10.1
|
||||||
golang.org/x/text v0.34.0
|
golang.org/x/text v0.34.0
|
||||||
google.golang.org/grpc v1.79.3
|
google.golang.org/grpc v1.80.0
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.2.0 // indirect
|
||||||
github.com/alicebob/miniredis/v2 v2.37.0 // indirect
|
github.com/alicebob/miniredis/v2 v2.37.0 // indirect
|
||||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec // indirect
|
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||||
github.com/alicebob/miniredis/v2 v2.37.0 h1:RheObYW32G1aiJIj81XVt78ZHJpHonHLHW7OLIshq68=
|
github.com/alicebob/miniredis/v2 v2.37.0 h1:RheObYW32G1aiJIj81XVt78ZHJpHonHLHW7OLIshq68=
|
||||||
@@ -51,8 +51,8 @@ github.com/go-openapi/spec v0.21.1-0.20250328170532-a3928469592e h1:auobAirzhPsL
|
|||||||
github.com/go-openapi/spec v0.21.1-0.20250328170532-a3928469592e/go.mod h1:NAKTe9SplQBxIUlHlsuId1jk1I7bWTVV/2q/GtdRi6g=
|
github.com/go-openapi/spec v0.21.1-0.20250328170532-a3928469592e/go.mod h1:NAKTe9SplQBxIUlHlsuId1jk1I7bWTVV/2q/GtdRi6g=
|
||||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
|
||||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
|
||||||
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/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
@@ -282,14 +282,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ type discovBuilder struct{}
|
|||||||
|
|
||||||
func (b *discovBuilder) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (
|
func (b *discovBuilder) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (
|
||||||
resolver.Resolver, error) {
|
resolver.Resolver, error) {
|
||||||
hosts := strings.FieldsFunc(targets.GetAuthority(target), func(r rune) bool {
|
hosts := strings.FieldsFunc(targets.GetHosts(target), func(r rune) bool {
|
||||||
return r == EndpointSepChar
|
return r == EndpointSepChar
|
||||||
})
|
})
|
||||||
sub, err := discov.NewSubscriber(hosts, targets.GetEndpoints(target))
|
sub, err := discov.NewSubscriber(hosts, targets.GetKey(target))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func TestDiscovBuilder_Build(t *testing.T) {
|
|||||||
for _, server := range servers.Servers {
|
for _, server := range servers.Servers {
|
||||||
addrs = append(addrs, server.Address)
|
addrs = append(addrs, server.Address)
|
||||||
}
|
}
|
||||||
u, err := url.Parse(fmt.Sprintf("%s://%s", DiscovScheme, strings.Join(addrs, ",")))
|
u, err := url.Parse(fmt.Sprintf("%s:///%s?key=test", DiscovScheme, strings.Join(addrs, ",")))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var b discovBuilder
|
var b discovBuilder
|
||||||
|
|||||||
@@ -17,3 +17,29 @@ func GetAuthority(target resolver.Target) string {
|
|||||||
func GetEndpoints(target resolver.Target) string {
|
func GetEndpoints(target resolver.Target) string {
|
||||||
return strings.Trim(target.URL.Path, slashSeparator)
|
return strings.Trim(target.URL.Path, slashSeparator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHosts returns the comma-separated etcd hosts from the target URL.
|
||||||
|
// It supports two formats:
|
||||||
|
// - New format (etcd:///h1:port,h2:port?key=k): hosts are in the URL path (empty authority)
|
||||||
|
// - Legacy format (etcd://h1:port/key): host is in the URL authority
|
||||||
|
func GetHosts(target resolver.Target) string {
|
||||||
|
if target.URL.Host == "" {
|
||||||
|
// New format: hosts encoded in URL path to avoid RFC 3986 authority issues
|
||||||
|
return GetEndpoints(target)
|
||||||
|
}
|
||||||
|
// Legacy format: single host in authority
|
||||||
|
return target.URL.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKey returns the etcd key from the target URL.
|
||||||
|
// It supports two formats:
|
||||||
|
// - New format (etcd:///h1:port,h2:port?key=k): key is in the "key" query parameter
|
||||||
|
// - Legacy format (etcd://h1:port/key): key is in the URL path
|
||||||
|
func GetKey(target resolver.Target) string {
|
||||||
|
if target.URL.Host == "" {
|
||||||
|
// New format: key is in the query parameter
|
||||||
|
return target.URL.Query().Get("key")
|
||||||
|
}
|
||||||
|
// Legacy format: key is in the path
|
||||||
|
return strings.Trim(target.URL.Path, slashSeparator)
|
||||||
|
}
|
||||||
|
|||||||
@@ -87,3 +87,83 @@ func TestGetEndpoints(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetHosts(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
url string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single host",
|
||||||
|
url: "etcd:///localhost:2379?key=foo",
|
||||||
|
want: "localhost:2379",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple hosts",
|
||||||
|
url: "etcd:///host1:2379,host2:2379,host3:2379?key=foo",
|
||||||
|
want: "host1:2379,host2:2379,host3:2379",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "legacy single host in authority",
|
||||||
|
url: "etcd://localhost:2379/my-service",
|
||||||
|
want: "localhost:2379",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
uri, err := url.Parse(test.url)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
target := resolver.Target{
|
||||||
|
URL: *uri,
|
||||||
|
}
|
||||||
|
assert.Equal(t, test.want, GetHosts(target))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetKey(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
url string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple key",
|
||||||
|
url: "etcd:///localhost:2379?key=my-service",
|
||||||
|
want: "my-service",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "key with slashes",
|
||||||
|
url: "etcd:///localhost:2379?key=%2Fgrpc%2Fmy-service",
|
||||||
|
want: "/grpc/my-service",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no key",
|
||||||
|
url: "etcd:///localhost:2379",
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "legacy key in path",
|
||||||
|
url: "etcd://localhost:2379/my-service",
|
||||||
|
want: "my-service",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "legacy key with leading slash",
|
||||||
|
url: "etcd://localhost:2379/grpc/my-service",
|
||||||
|
want: "grpc/my-service",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
uri, err := url.Parse(test.url)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
target := resolver.Target{
|
||||||
|
URL: *uri,
|
||||||
|
}
|
||||||
|
assert.Equal(t, test.want, GetKey(target))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package resolver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/zrpc/resolver/internal"
|
"github.com/zeromicro/go-zero/zrpc/resolver/internal"
|
||||||
@@ -14,7 +15,9 @@ func BuildDirectTarget(endpoints []string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BuildDiscovTarget returns a string that represents the given endpoints with discov schema.
|
// BuildDiscovTarget returns a string that represents the given endpoints with discov schema.
|
||||||
|
// The format is etcd:///host1:port,host2:port?key=<etcd-key> to avoid placing comma-separated
|
||||||
|
// hosts in the URI authority, which Go 1.26+ rejects per RFC 3986.
|
||||||
func BuildDiscovTarget(endpoints []string, key string) string {
|
func BuildDiscovTarget(endpoints []string, key string) string {
|
||||||
return fmt.Sprintf("%s://%s/%s", internal.EtcdScheme,
|
return fmt.Sprintf("%s:///%s?key=%s", internal.EtcdScheme,
|
||||||
strings.Join(endpoints, internal.EndpointSep), key)
|
strings.Join(endpoints, internal.EndpointSep), url.QueryEscape(key))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,5 +13,10 @@ func TestBuildDirectTarget(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuildDiscovTarget(t *testing.T) {
|
func TestBuildDiscovTarget(t *testing.T) {
|
||||||
target := BuildDiscovTarget([]string{"localhost:123", "localhost:456"}, "foo")
|
target := BuildDiscovTarget([]string{"localhost:123", "localhost:456"}, "foo")
|
||||||
assert.Equal(t, "etcd://localhost:123,localhost:456/foo", target)
|
assert.Equal(t, "etcd:///localhost:123,localhost:456?key=foo", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildDiscovTargetWithSlashKey(t *testing.T) {
|
||||||
|
target := BuildDiscovTarget([]string{"localhost:2379"}, "/grpc/my-service")
|
||||||
|
assert.Equal(t, "etcd:///localhost:2379?key=%2Fgrpc%2Fmy-service", target)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user