Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion cmd/src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ The commands are:
search search for results on Sourcegraph
search-jobs manages search jobs
serve-git serves your local git repositories over HTTP for Sourcegraph to pull
tool exposes tools for AI agents to interact with Sourcegraph (EXPERIMENTAL)
users,user manages users
codeowners manages code ownership information
version display and compare the src-cli version against the recommended version for your instance
Expand Down
115 changes: 115 additions & 0 deletions cmd/src/mcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package main

import (
"flag"
"fmt"

"github.com/sourcegraph/src-cli/internal/mcp"
)

func init() {
flagSet := flag.NewFlagSet("mcp", flag.ExitOnError)
commands = append(commands, &command{
flagSet: flagSet,
handler: mcpMain,
})
}
func mcpMain(args []string) error {
fmt.Println("NOTE: This command is still experimental")
tools, err := mcp.LoadToolDefinitions()

Check failure on line 19 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / scip-go

undefined: mcp.LoadToolDefinitions

Check failure on line 19 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-lint

undefined: mcp.LoadToolDefinitions

Check failure on line 19 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-test (macos-latest)

undefined: mcp.LoadToolDefinitions
if err != nil {
return err
}

subcmd := args[0]
if subcmd == "list-tools" {
fmt.Println("The following tools are available:")
for name := range tools {
fmt.Printf(" • %s\n", name)
}
fmt.Println("\nUSAGE:")
fmt.Printf(" • Invoke a tool\n")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: Why not just a normal ascii * instead of unicode stars? Ignore me if I am too much of an ASCII lover.

fmt.Printf(" src mcp <tool-name> <flags>\n")
fmt.Printf("\n • View the Input / Output Schema of a tool\n")
fmt.Printf(" src mcp <tool-name> schema\n")
fmt.Printf("\n • List the available flags of a tool\n")
fmt.Printf(" src mcp <tool-name> -h\n")
fmt.Printf("\n • View the Input / Output Schema of a tool\n")
fmt.Printf(" src mcp <tool-name> schema\n")
return nil
}

tool, ok := tools[subcmd]
if !ok {
return fmt.Errorf("tool definition for %q not found - run src mcp list-tools to see a list of available tools", subcmd)
}

flagArgs := args[1:] // skip subcommand name
if len(args) > 1 && args[1] == "schema" {
return printSchemas(tool)
}

flags, vars, err := mcp.BuildArgFlagSet(tool)
if err != nil {
return err
}
if err := flags.Parse(flagArgs); err != nil {
return err
}
mcp.DerefFlagValues(vars)

if err := validateToolArgs(tool.InputSchema, args, vars); err != nil {
return err
}

apiClient := cfg.apiClient(nil, flags.Output())
return handleMcpTool(context.Background(), apiClient, tool, vars)

Check failure on line 66 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / scip-go

undefined: context

Check failure on line 66 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-lint

undefined: context

Check failure on line 66 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-test (macos-latest)

undefined: context
}

func printSchemas(tool *mcp.ToolDef) error {
input, err := json.MarshalIndent(tool.InputSchema, "", " ")

Check failure on line 70 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / scip-go

undefined: json

Check failure on line 70 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-lint

undefined: json

Check failure on line 70 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-test (macos-latest)

undefined: json
if err != nil {
return err
}
output, err := json.MarshalIndent(tool.OutputSchema, "", " ")

Check failure on line 74 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / scip-go

undefined: json

Check failure on line 74 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-lint

undefined: json

Check failure on line 74 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-test (macos-latest)

undefined: json
if err != nil {
return err
}

fmt.Printf("Input:\n%v\nOutput:\n%v\n", string(input), string(output))
return nil
}

func validateToolArgs(inputSchema mcp.SchemaObject, args []string, vars map[string]any) error {
for _, reqName := range inputSchema.Required {
if vars[reqName] == nil {
return errors.Newf("no value provided for required flag --%s", reqName)

Check failure on line 86 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / scip-go

undefined: errors

Check failure on line 86 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-lint

undefined: errors

Check failure on line 86 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-test (macos-latest)

undefined: errors
}
}

if len(args) < len(inputSchema.Required) {
return errors.Newf("not enough arguments provided - the following flags are required:\n%s", strings.Join(inputSchema.Required, "\n"))

Check failure on line 91 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / scip-go

undefined: strings

Check failure on line 91 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / scip-go

undefined: errors

Check failure on line 91 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-lint

undefined: strings

Check failure on line 91 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-lint

undefined: errors

Check failure on line 91 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-test (macos-latest)

undefined: strings

Check failure on line 91 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-test (macos-latest)

undefined: errors
}

return nil
}

func handleMcpTool(ctx context.Context, client api.Client, tool *mcp.ToolDef, vars map[string]any) error {

Check failure on line 97 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / scip-go

undefined: api

Check failure on line 97 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / scip-go

undefined: context

Check failure on line 97 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-lint

undefined: api

Check failure on line 97 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-lint

undefined: context

Check failure on line 97 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-test (macos-latest)

undefined: api

Check failure on line 97 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-test (macos-latest)

undefined: context
resp, err := mcp.DoToolRequest(ctx, client, tool, vars)
if err != nil {
return err
}

result, err := mcp.DecodeToolResponse(resp)
if err != nil {
return err
}
defer resp.Body.Close()

output, err := json.MarshalIndent(result, "", " ")

Check failure on line 109 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / scip-go

undefined: json

Check failure on line 109 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-lint

undefined: json

Check failure on line 109 in cmd/src/mcp.go

View workflow job for this annotation

GitHub Actions / go-test (macos-latest)

undefined: json
if err != nil {
return err
}
fmt.Println(string(output))
return nil
}
19 changes: 10 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ require (
github.com/sourcegraph/scip v0.6.1
github.com/sourcegraph/sourcegraph/lib v0.0.0-20240709083501-1af563b61442
github.com/stretchr/testify v1.11.1
golang.org/x/net v0.46.0
golang.org/x/net v0.47.0
golang.org/x/sync v0.18.0
google.golang.org/api v0.256.0
google.golang.org/protobuf v1.36.10
Expand Down Expand Up @@ -67,15 +67,15 @@ require (
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v24.0.4+incompatible // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v25.0.6+incompatible // indirect
github.com/docker/docker v28.0.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/felixge/fgprof v0.9.3 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-chi/chi/v5 v5.0.10 // indirect
github.com/go-chi/chi/v5 v5.2.2 // indirect
github.com/go-jose/go-jose/v4 v4.1.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gofrs/uuid/v5 v5.0.0 // indirect
Expand All @@ -91,11 +91,12 @@ require (
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/rs/cors v1.9.0 // indirect
github.com/rs/cors v1.11.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/beaut v0.0.0-20240611013027-627e4c25335a // indirect
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
Expand All @@ -116,7 +117,7 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 // indirect
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
Expand Down Expand Up @@ -216,14 +217,14 @@ require (
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/mod v0.29.0 // indirect
golang.org/x/oauth2 v0.33.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.30.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.37.0 // indirect
golang.org/x/tools v0.38.0 // indirect
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.76.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect; direct
Expand Down
38 changes: 20 additions & 18 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ github.com/docker/cli v24.0.4+incompatible h1:Y3bYF9ekNTm2VFz5U/0BlMdJy73D+Y1iAA
github.com/docker/cli v24.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg=
github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.0.0+incompatible h1:Olh0KS820sJ7nPsBKChVhk5pzqcwDR15fumfAd/p9hM=
github.com/docker/docker v28.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
Expand Down Expand Up @@ -188,8 +188,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI=
Expand Down Expand Up @@ -344,6 +344,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
Expand Down Expand Up @@ -402,8 +404,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
Expand Down Expand Up @@ -513,25 +515,25 @@ 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-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.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -563,8 +565,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 h1:dHQOQddU4YHS5gY33/6klKjq7Gp3WwMyOXGNp5nzRj8=
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE=
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU=
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE=
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.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
Expand All @@ -574,8 +576,8 @@ 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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand All @@ -586,8 +588,8 @@ golang.org/x/tools v0.0.0-20200624163319-25775e59acb7/go.mod h1:EkVYQZoAsY45+roY
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.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
94 changes: 94 additions & 0 deletions internal/mcp/mcp_args.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package mcp

import (
"flag"
"fmt"
"reflect"
"strings"

"github.com/sourcegraph/sourcegraph/lib/errors"
)

var _ flag.Value = (*strSliceFlag)(nil)

type strSliceFlag struct {
vals []string
}

func (s *strSliceFlag) Set(v string) error {
s.vals = append(s.vals, v)
return nil
}

func (s *strSliceFlag) String() string {
return strings.Join(s.vals, ",")
}

func DerefFlagValues(vars map[string]any) {
for k, v := range vars {
rfl := reflect.ValueOf(v)
if rfl.Kind() == reflect.Pointer {
vv := rfl.Elem().Interface()
if slice, ok := vv.(strSliceFlag); ok {
vv = slice.vals
}
if isNil(vv) {
delete(vars, k)
} else {
vars[k] = vv
}
}
}
}

func isNil(v any) bool {
if v == nil {
return true
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Slice, reflect.Map, reflect.Pointer, reflect.Interface:
return rv.IsNil()
default:
return false
}
}

func BuildArgFlagSet(tool *ToolDef) (*flag.FlagSet, map[string]any, error) {
if tool == nil {
return nil, nil, errors.New("cannot build flagset on nil Tool Definition")
}
fs := flag.NewFlagSet(tool.Name, flag.ContinueOnError)
flagVars := map[string]any{}

for name, pVal := range tool.InputSchema.Properties {
switch pv := pVal.(type) {
case *SchemaPrimitive:
switch pv.Type {
case "integer":
dst := fs.Int(name, 0, pv.Description)
flagVars[name] = dst

case "boolean":
dst := fs.Bool(name, false, pv.Description)
flagVars[name] = dst
case "string":
dst := fs.String(name, "", pv.Description)
flagVars[name] = dst
default:
return nil, nil, fmt.Errorf("unknown schema primitive kind %q", pv.Type)

}
case *SchemaArray:
strSlice := new(strSliceFlag)
fs.Var(strSlice, name, pv.Description)
flagVars[name] = strSlice
case *SchemaObject:
// TODO(burmudar): we can support SchemaObject as part of stdin echo '{ stuff }' | sg mcp commit-search
// not supported yet
// Also support sg mcp commit-search --json '{ stuff }'
}
}

return fs, flagVars, nil
}
Loading
Loading