diff --git a/cmd/mcp/mcp.go b/cmd/mcp/mcp.go new file mode 100644 index 0000000..ba72c7d --- /dev/null +++ b/cmd/mcp/mcp.go @@ -0,0 +1,100 @@ +package mcp + +import ( + "fmt" + + "github.com/metal-stack/api/go/metalstack/admin/v2/adminv2mcp" + "github.com/metal-stack/api/go/metalstack/api/v2/apiv2mcp" + "github.com/metal-stack/cli/cmd/config" + "github.com/metal-stack/v" + "github.com/modelcontextprotocol/go-sdk/mcp" + "github.com/redpanda-data/protoc-gen-go-mcp/pkg/runtime" + "github.com/redpanda-data/protoc-gen-go-mcp/pkg/runtime/gosdk" + + "github.com/spf13/cobra" +) + +var long = fmt.Sprintf(` +Use %q to serve as mcp server. You must configure your coding agent to make use of the mcp server. + +Example opencode.json: + +"mcp": { + "metal": { + "type": "local", + "command": [ + %q, + "mcp" + ], + "enabled": true, + } +} + +Then login with %q and start you coding agent and ask questions like: + +- list all metal partitions +- give me all available metal sizes and images +- create a ip address, ask me questions +`, config.BinaryName, config.BinaryName, config.BinaryName) + +func NewMCPCmd(c *config.Config) *cobra.Command { + + cmd := &cobra.Command{ + Use: "mcp", + Short: "start mcp server", + Long: long, + RunE: func(cmd *cobra.Command, args []string) error { + raw, mcps := gosdk.NewServer("metal-stack.io mcp server", v.V.String()) + + forwardToClients(c, mcps) + + if err := raw.Run(cmd.Context(), &mcp.StdioTransport{}); err != nil { + return err + } + return nil + }, + } + return cmd +} + +func forwardToClients(c *config.Config, mcps runtime.MCPServer) { + // Be prepared for later opts. + var opts []runtime.Option + // API Clients + apiv2mcp.ForwardToAuditServiceClient(mcps, c.Client.Apiv2().Audit(), opts...) + apiv2mcp.ForwardToFilesystemServiceClient(mcps, c.Client.Apiv2().Filesystem(), opts...) + apiv2mcp.ForwardToHealthServiceClient(mcps, c.Client.Apiv2().Health(), opts...) + apiv2mcp.ForwardToImageServiceClient(mcps, c.Client.Apiv2().Image(), opts...) + apiv2mcp.ForwardToIPServiceClient(mcps, c.Client.Apiv2().IP(), opts...) + apiv2mcp.ForwardToMachineServiceClient(mcps, c.Client.Apiv2().Machine(), opts...) + apiv2mcp.ForwardToMethodServiceClient(mcps, c.Client.Apiv2().Method(), opts...) + apiv2mcp.ForwardToNetworkServiceClient(mcps, c.Client.Apiv2().Network(), opts...) + apiv2mcp.ForwardToPartitionServiceClient(mcps, c.Client.Apiv2().Partition(), opts...) + apiv2mcp.ForwardToProjectServiceClient(mcps, c.Client.Apiv2().Project(), opts...) + apiv2mcp.ForwardToSizeImageConstraintServiceClient(mcps, c.Client.Apiv2().SizeImageConstraint(), opts...) + apiv2mcp.ForwardToSizeReservationServiceClient(mcps, c.Client.Apiv2().SizeReservation(), opts...) + apiv2mcp.ForwardToSizeServiceClient(mcps, c.Client.Apiv2().Size(), opts...) + apiv2mcp.ForwardToTenantServiceClient(mcps, c.Client.Apiv2().Tenant(), opts...) + apiv2mcp.ForwardToTokenServiceClient(mcps, c.Client.Apiv2().Token(), opts...) + apiv2mcp.ForwardToUserServiceClient(mcps, c.Client.Apiv2().User(), opts...) + apiv2mcp.ForwardToVersionServiceClient(mcps, c.Client.Apiv2().Version(), opts...) + + // Admin Clients + adminv2mcp.ForwardToAuditServiceClient(mcps, c.Client.Adminv2().Audit(), opts...) + adminv2mcp.ForwardToComponentServiceClient(mcps, c.Client.Adminv2().Component(), opts...) + adminv2mcp.ForwardToFilesystemServiceClient(mcps, c.Client.Adminv2().Filesystem(), opts...) + adminv2mcp.ForwardToImageServiceClient(mcps, c.Client.Adminv2().Image(), opts...) + adminv2mcp.ForwardToIPServiceClient(mcps, c.Client.Adminv2().IP(), opts...) + adminv2mcp.ForwardToMachineServiceClient(mcps, c.Client.Adminv2().Machine(), opts...) + adminv2mcp.ForwardToNetworkServiceClient(mcps, c.Client.Adminv2().Network(), opts...) + adminv2mcp.ForwardToPartitionServiceClient(mcps, c.Client.Adminv2().Partition(), opts...) + adminv2mcp.ForwardToProjectServiceClient(mcps, c.Client.Adminv2().Project(), opts...) + adminv2mcp.ForwardToSizeImageConstraintServiceClient(mcps, c.Client.Adminv2().SizeImageConstraint(), opts...) + adminv2mcp.ForwardToSizeReservationServiceClient(mcps, c.Client.Adminv2().SizeReservation(), opts...) + adminv2mcp.ForwardToSizeServiceClient(mcps, c.Client.Adminv2().Size(), opts...) + adminv2mcp.ForwardToSwitchServiceClient(mcps, c.Client.Adminv2().Switch(), opts...) + adminv2mcp.ForwardToTaskServiceClient(mcps, c.Client.Adminv2().Task(), opts...) + adminv2mcp.ForwardToTenantServiceClient(mcps, c.Client.Adminv2().Tenant(), opts...) + adminv2mcp.ForwardToTokenServiceClient(mcps, c.Client.Adminv2().Token(), opts...) + adminv2mcp.ForwardToVPNServiceClient(mcps, c.Client.Adminv2().VPN(), opts...) +} diff --git a/cmd/mcp/mcp_test.go b/cmd/mcp/mcp_test.go new file mode 100644 index 0000000..144ce8e --- /dev/null +++ b/cmd/mcp/mcp_test.go @@ -0,0 +1,47 @@ +package mcp + +import ( + "log" + "os/exec" + "testing" + + "github.com/modelcontextprotocol/go-sdk/mcp" + "github.com/stretchr/testify/require" +) + +func TestMCPServer(t *testing.T) { + t.SkipNow() + ctx := t.Context() + // Create a new client, with no features. + client := mcp.NewClient(&mcp.Implementation{Name: "mcp-client", Version: "v1.0.0"}, nil) + + // Connect to a server over stdin/stdout. + transport := &mcp.CommandTransport{Command: exec.Command("/home/stefan/bin/metalctlv2", "mcp")} + session, err := client.Connect(ctx, transport, nil) + require.NoError(t, err) + defer func() { + _ = session.Close() + }() + + tools, err := session.ListTools(ctx, &mcp.ListToolsParams{}) + require.NoError(t, err) + for _, tool := range tools.Tools { + t.Logf("%s", tool.Name) + t.Logf("%#v", tool) + } + + // Call a tool on the server. + params := &mcp.CallToolParams{ + Name: "metalstack_api_v2_PartitionService_List", + Arguments: map[string]any{}, + } + res, err := session.CallTool(ctx, params) + require.NoError(t, err) + if res.IsError { + t.Logf("tool failed %v", res.Content) + } + for _, c := range res.Content { + log.Print(c.(*mcp.TextContent).Text) + } + t.Fail() +} diff --git a/cmd/root.go b/cmd/root.go index 8041aae..a14a0a2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -9,6 +9,7 @@ import ( adminv2 "github.com/metal-stack/cli/cmd/admin/v2" apiv2 "github.com/metal-stack/cli/cmd/api/v2" + "github.com/metal-stack/cli/cmd/mcp" "github.com/metal-stack/cli/cmd/completion" "github.com/metal-stack/cli/cmd/config" @@ -95,7 +96,7 @@ func NewRootCmd(c *config.Config) *cobra.Command { }, } - rootCmd.AddCommand(newContextCmd(c), markdownCmd, newLoginCmd(c), newLogoutCmd(c)) + rootCmd.AddCommand(newContextCmd(c), markdownCmd, newLoginCmd(c), newLogoutCmd(c), mcp.NewMCPCmd(c)) adminv2.AddCmds(rootCmd, c) apiv2.AddCmds(rootCmd, c) diff --git a/docs/metalctlv2.md b/docs/metalctlv2.md index b1579d5..649c399 100644 --- a/docs/metalctlv2.md +++ b/docs/metalctlv2.md @@ -28,6 +28,7 @@ cli for managing entities in metal-stack * [metalctlv2 login](metalctlv2_login.md) - login * [metalctlv2 logout](metalctlv2_logout.md) - logout * [metalctlv2 markdown](metalctlv2_markdown.md) - create markdown documentation +* [metalctlv2 mcp](metalctlv2_mcp.md) - start mcp server * [metalctlv2 network](metalctlv2_network.md) - manage network entities * [metalctlv2 partition](metalctlv2_partition.md) - manage partition entities * [metalctlv2 project](metalctlv2_project.md) - manage project entities diff --git a/go.mod b/go.mod index 75251b0..a07459d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/metal-stack/cli -go 1.26.0 +go 1.26.1 require ( connectrpc.com/connect v1.20.0 @@ -9,20 +9,23 @@ require ( github.com/fatih/color v1.19.0 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 - github.com/metal-stack/api v0.4.4 + github.com/metal-stack/api v0.4.5-0.20260804062648-51225da9541a github.com/metal-stack/metal-lib v0.26.1 github.com/metal-stack/v v1.0.3 + github.com/modelcontextprotocol/go-sdk v1.7.0 + github.com/redpanda-data/protoc-gen-go-mcp v0.0.0-20260729122341-6334690a62b6 github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 - google.golang.org/grpc v1.82.1 + google.golang.org/grpc v1.83.0 google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af sigs.k8s.io/yaml v1.6.0 ) require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1 // indirect + buf.build/gen/go/redpandadata/common/protocolbuffers/go v1.36.11-20260323171043-6e06f84ad823.1 // indirect buf.build/go/protovalidate v1.2.0 // indirect buf.build/go/protoyaml v0.7.0 // indirect cel.dev/expr v0.25.2 // indirect @@ -41,6 +44,7 @@ require ( github.com/goccy/go-yaml v1.19.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/cel-go v0.30.0 // indirect + github.com/google/jsonschema-go v0.4.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.19.1 // indirect github.com/klauspost/connect-compress/v2 v2.1.1 // indirect @@ -55,20 +59,27 @@ require ( github.com/olekukonko/tablewriter v1.1.4 // indirect github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/redpanda-data/common-go/api v0.0.0-20260805234448-c124c21fdb6b // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect + github.com/segmentio/asm v1.2.1 // indirect + github.com/segmentio/encoding v0.5.4 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/exp v0.0.0-20260727155853-b88d891fe743 // indirect golang.org/x/net v0.57.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.40.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260727163830-6c54dddc4772 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260727163830-6c54dddc4772 // indirect + golang.org/x/time v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260729162451-8efbd57d26e0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apimachinery v0.36.3 // indirect diff --git a/go.sum b/go.sum index b2fceca..2419d7b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1 h1:fXh8CsdNpjRr8R5vFdqtIxPt/Lno2IIJlYOdZBIZn0w= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM= +buf.build/gen/go/redpandadata/common/protocolbuffers/go v1.36.11-20260323171043-6e06f84ad823.1 h1:AZVH8d9Xh0ar1BbxDiY+2urib65GfAcT8rPZfM++N/Q= +buf.build/gen/go/redpandadata/common/protocolbuffers/go v1.36.11-20260323171043-6e06f84ad823.1/go.mod h1:3w7EzexwlL6PIFGbbeKZ0yHfUlAmI0aBVzF/QoFb8Cg= buf.build/go/protovalidate v1.2.0 h1:DQVrUWkmGTBij+kOYv/x2LLxwcLaGKMdzShj1/6/3H0= buf.build/go/protovalidate v1.2.0/go.mod h1:7rYiQEhqvAipoazpVNBBH2S2f8bjG4huMVy1V2Yofn4= buf.build/go/protoyaml v0.7.0 h1:z4oVoFicbpPefhT7WAykxUdfp0yEQlhMQ2mCZOY5V38= @@ -55,6 +57,8 @@ github.com/google/cel-go v0.30.0 h1:ll54AkzKunWkBn9wSoiUXbFZXYZTkdJGNXTBXUoolGo= github.com/google/cel-go v0.30.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/jsonschema-go v0.4.3 h1:/DBOLZTfDow7pe2GmaJNhltueGTtDKICi8V8p+DQPd0= +github.com/google/jsonschema-go v0.4.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -73,14 +77,16 @@ github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsRe github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= github.com/mattn/go-runewidth v0.0.27 h1:Feg/Oou5zI/wnpgDF6omIU0OokC9GxLC/WRknhVlIR0= github.com/mattn/go-runewidth v0.0.27/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8= -github.com/metal-stack/api v0.4.4 h1:NwJKCFHnbmsU7DjHLtBmpdPuPniXc2wDabHjdpXk3qA= -github.com/metal-stack/api v0.4.4/go.mod h1:E7f2GkKNSr4vBxhQwWrr/Mjnf4ctPqCX2dpDtXBqkBk= +github.com/metal-stack/api v0.4.5-0.20260804062648-51225da9541a h1:oXU+7KZ0QocX+7DLTEPPiYPED/t+/APSz83z6Io6i9c= +github.com/metal-stack/api v0.4.5-0.20260804062648-51225da9541a/go.mod h1:ZbRkorVh9W6Pv6v8mcBo9YyYIEry1vN9II1YgEHmavc= github.com/metal-stack/metal-lib v0.26.1 h1:wMrhENm/HPq0t7cjCkhzepGD6dyxmX/4IMsaJZea874= github.com/metal-stack/metal-lib v0.26.1/go.mod h1:tnx4MM5oml10EMN6Nq6oFSbjOKB9B27WUZQUyZ4MywA= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= github.com/metal-stack/v v1.0.3/go.mod h1:YTahEu7/ishwpYKnp/VaW/7nf8+PInogkfGwLcGPdXg= github.com/minio/minlz v1.2.0 h1:6IOBuiHg04QxvbFfgFLT/9sMaO/UhL7S+ApW1mK8q5A= github.com/minio/minlz v1.2.0/go.mod h1:Ls9H7nlkASeCcdl5thjVD5Eraj6z+zGa7xtq57jIKD4= +github.com/modelcontextprotocol/go-sdk v1.7.0 h1:yqjY2dsbKAC0LSuWZVBMrHgiG8ukXv6NRo0JiALay44= +github.com/modelcontextprotocol/go-sdk v1.7.0/go.mod h1:dL7u98E/zjJTGzEq+j30jQ8K2k1mb6LeAH4inEcSGts= github.com/oklog/ulid/v2 v2.1.2 h1:IEclFb9JNvzYA6MW2SCxbLzcHTVsfqm3PrqGQJH5zec= github.com/oklog/ulid/v2 v2.1.2/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= @@ -91,11 +97,17 @@ github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8= github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw= github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I= github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY= +github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= +github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redpanda-data/common-go/api v0.0.0-20260805234448-c124c21fdb6b h1:37u906bF7SsdlHuFwkDjFY/NAe6vKkMJUMdGNB9FBdY= +github.com/redpanda-data/common-go/api v0.0.0-20260805234448-c124c21fdb6b/go.mod h1:X4iTnBNz6LJ7KwyOkW/7xh+pBS8tuqFWUwECPkVDjIo= +github.com/redpanda-data/protoc-gen-go-mcp v0.0.0-20260729122341-6334690a62b6 h1:ThmYtRYkopbcf1vhKlsE6OEMQWHl1VrIxk9ZgLqJpgw= +github.com/redpanda-data/protoc-gen-go-mcp v0.0.0-20260729122341-6334690a62b6/go.mod h1:qCv9zvs6wyK9+EyY0auYApUdOEgi6TVM6aWQ7Byahuc= github.com/rodaine/protogofakeit v0.1.1 h1:ZKouljuRM3A+TArppfBqnH8tGZHOwM/pjvtXe9DaXH8= github.com/rodaine/protogofakeit v0.1.1/go.mod h1:pXn/AstBYMaSfc1/RqH3N82pBuxtWgejz1AlYpY1mI0= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -104,6 +116,10 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= +github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= +github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0= +github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= @@ -121,6 +137,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= @@ -130,16 +148,24 @@ golang.org/x/exp v0.0.0-20260727155853-b88d891fe743 h1:ex206bKw+v3K0dm3andkrIF+i golang.org/x/exp v0.0.0-20260727155853-b88d891fe743/go.mod h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q= golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= -google.golang.org/genproto/googleapis/api v0.0.0-20260727163830-6c54dddc4772 h1:4namukbyF7JY83aWHQwi9J5ugNTnDReLJ9ZcpqOpRB4= -google.golang.org/genproto/googleapis/api v0.0.0-20260727163830-6c54dddc4772/go.mod h1:1brfde68Npq6+WA75c1EHWPijZEG1kMus61ygPZfn4A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260727163830-6c54dddc4772 h1:zuslGE3FGxH0hC6veLvSLME3TZzun9MQzjYwo1CBN+k= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260727163830-6c54dddc4772/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE= -google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= +google.golang.org/genproto/googleapis/api v0.0.0-20260729162451-8efbd57d26e0 h1:ybvH/ZpOcpCrjtkb7oW/fdlzbEmRVeumw19SRQmNFKU= +google.golang.org/genproto/googleapis/api v0.0.0-20260729162451-8efbd57d26e0/go.mod h1:HJ9MpJLeDSstBkx1LILTpd5f41ADSMZcTPypw02qEGw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0 h1:mJiOtnGp0k/BcSgdu03G2NwnscCfCH+h2QKUBZr18KI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ= +google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=