Skip to content
Open
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
43 changes: 43 additions & 0 deletions cmd/src/mcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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()
if err != nil {
return err
}

subcmd := args[0]
if subcmd == "list-tools" {
fmt.Println("Available tools")
for name := range tools {
fmt.Printf("- %s\n", name)
}
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)
}
return handleMcpTool(tool, args[1:])
}

func handleMcpTool(tool *mcp.ToolDef, args []string) error {
fmt.Printf("handling tool %q args: %+v", tool.Name, args)
return nil
}
21 changes: 16 additions & 5 deletions internal/mcp/mcp_parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ package mcp
import (
_ "embed"
"encoding/json"
"fmt"
"strings"

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

//go:embed mcp_tools.json
var _ []byte
var mcpToolListJSON []byte

type ToolDef struct {
Name string `json:"name"`
Expand Down Expand Up @@ -67,7 +67,11 @@ type parser struct {
errors []error
}

func LoadToolDefinitions(data []byte) (map[string]*ToolDef, error) {
func LoadToolDefinitions() (map[string]*ToolDef, error) {
return loadToolDefinitions(mcpToolListJSON)
}

func loadToolDefinitions(data []byte) (map[string]*ToolDef, error) {
defs := struct {
Tools []struct {
Name string `json:"name"`
Expand All @@ -85,7 +89,8 @@ func LoadToolDefinitions(data []byte) (map[string]*ToolDef, error) {
parser := &parser{}

for _, t := range defs.Tools {
tools[t.Name] = &ToolDef{
name := normalizeToolName(t.Name)
tools[name] = &ToolDef{
Name: t.Name,
Description: t.Description,
InputSchema: parser.parseRootSchema(t.InputSchema),
Expand Down Expand Up @@ -157,10 +162,16 @@ func (p *parser) parseProperties(props map[string]json.RawMessage) map[string]Sc
for name, raw := range props {
var r RawSchema
if err := json.Unmarshal(raw, &r); err != nil {
p.errors = append(p.errors, fmt.Errorf("failed to parse property %q: %w", name, err))
p.errors = append(p.errors, errors.Newf("failed to parse property %q: %w", name, err))
continue
}
res[name] = p.parseSchema(&r)
}
return res
}

// normalizeToolName takes mcp tool names like 'sg_keyword_search' and normalizes it to 'keyword-search"
func normalizeToolName(toolName string) string {
toolName, _ = strings.CutPrefix(toolName, "sg_")
return strings.ReplaceAll(toolName, "_", "-")
}
5 changes: 3 additions & 2 deletions internal/mcp/mcp_parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func TestLoadToolDefinitions(t *testing.T) {
]
}`)

tools, err := LoadToolDefinitions(toolJSON)
tools, err := loadToolDefinitions(toolJSON)
if err != nil {
t.Fatalf("Failed to load tool definitions: %v", err)
}
Expand All @@ -46,7 +46,8 @@ func TestLoadToolDefinitions(t *testing.T) {
t.Fatalf("Expected 1 tool, got %d", len(tools))
}

tool := tools["test_tool"]
// Temporary: map keys have normalized names
tool := tools["test-tool"]
if tool == nil {
t.Fatal("Tool 'test_tool' not found")
}
Expand Down
Loading