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
21 changes: 20 additions & 1 deletion cmd/sin-code/internal/mcpclient/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,28 @@
}
return cfg
}
// goNative returns a ServerConfig for a Go-native skill. It prefers the
// binary built inside SIN_SKILLS_DIR/<repo>/sin-websearch so that skillmgr
// can install and run the skill without requiring the user to put the binary
// on PATH. Falls back to the binary name on PATH if no local checkout exists.
goNative := func(repo, binary string, args ...string) ServerConfig {
name := shortName(repo)
cfg := ServerConfig{Name: name, Transport: "stdio", Args: args}
if skillsDir != "" {
localBin := filepath.Join(skillsDir, repo, binary)
if _, err := os.Stat(localBin); err == nil {

Check failure

Code scanning / gosec

Path traversal via taint analysis Error

Path traversal via taint analysis
cfg.Command = localBin
} else {
cfg.Command = binary
}
} else {
cfg.Command = binary
}
return cfg
}
return []ServerConfig{
// web_search_bundle is the Go-native successor to SIN-Code-Websearch-Skill.
{Name: "websearch", Transport: "stdio", Command: "sin-websearch", Args: []string{"serve"}},
goNative("web_search_bundle", "sin-websearch", "serve"),
py("SIN-Code-Scheduler-Skill"),
py("SIN-Code-Goal-Mode-Skill"),
py("SIN-Code-Grill-Me-Skill"),
Expand Down
49 changes: 49 additions & 0 deletions cmd/sin-code/internal/mcpclient/registry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
// Purpose: tests for the built-in ecosystem registry in mcpclient.
package mcpclient

import (
"os"
"path/filepath"
"testing"
)

func TestDefaultServersWebsearchUsesLocalBinaryWhenPresent(t *testing.T) {
dir := t.TempDir()
bin := filepath.Join(dir, "web_search_bundle", "sin-websearch")
if err := os.MkdirAll(filepath.Dir(bin), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(bin, []byte("#!/bin/sh\necho fake"), 0o755); err != nil {
t.Fatal(err)
}

t.Setenv("SIN_SKILLS_DIR", dir)
for _, s := range DefaultServers() {
if s.Name != "websearch" {
continue
}
if s.Command != bin {
t.Fatalf("websearch command should use local binary %q, got %q", bin, s.Command)
}
if len(s.Args) != 1 || s.Args[0] != "serve" {
t.Fatalf("websearch args should be [serve], got %v", s.Args)
}
return
}
t.Fatal("websearch server not found in DefaultServers")
}

func TestDefaultServersWebsearchFallsBackToPathBinary(t *testing.T) {
t.Setenv("SIN_SKILLS_DIR", "")
for _, s := range DefaultServers() {
if s.Name != "websearch" {
continue
}
if s.Command != "sin-websearch" {
t.Fatalf("websearch command should fall back to %q, got %q", "sin-websearch", s.Command)
}
return
}
t.Fatal("websearch server not found in DefaultServers")
}
5 changes: 3 additions & 2 deletions cmd/sin-code/internal/skillmgr/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@ func verifyEntrypoint(ctx context.Context, dir string) (bool, string) {
return true, "node entrypoint (package.json)"
}
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
// Go-native skill: verify it compiles.
cmd := exec.CommandContext(ctx, "go", "build", "./cmd/sin-websearch")
// Go-native skill: build the binary into the repo root so the MCP
// registry can use the full path (SIN_SKILLS_DIR/<repo>/<binary>).
cmd := exec.CommandContext(ctx, "go", "build", "-o", "sin-websearch", "./cmd/sin-websearch")
cmd.Dir = dir
if _, err := cmd.CombinedOutput(); err != nil {
return false, fmt.Sprintf("go entrypoint exists but build failed: %v", err)
Expand Down
Loading