- Introduce new MCP and Robot command groups in the CLI, allowing for better organization of package management commands. - Add corresponding command descriptions for MCP and Robot functionalities to improve user guidance. - Update the agent command group to include additional commands for enhanced agent management. - Modify .gitignore to exclude specific design markdown files from version control. - Add environment variable for test application path in GitHub workflows to streamline testing setup.
89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
package agent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
goujson "github.com/yaoapp/gou/json"
|
|
"github.com/yaoapp/yao/registry/manager/common"
|
|
)
|
|
|
|
// packageYao represents the assistant's package.yao DSL (subset of fields we care about).
|
|
type packageYao struct {
|
|
MCP *mcpConfig `json:"mcp,omitempty"`
|
|
Agents []string `json:"agents,omitempty"`
|
|
Uses json.RawMessage `json:"uses,omitempty"`
|
|
}
|
|
|
|
type mcpConfig struct {
|
|
Servers map[string]mcpServerEntry `json:"servers,omitempty"`
|
|
}
|
|
|
|
type mcpServerEntry struct {
|
|
ServerID string `json:"server_id,omitempty"`
|
|
}
|
|
|
|
// ScanDependencies scans an assistant directory's package.yao for external dependencies.
|
|
// It finds MCP dependencies from mcp.servers and returns them as "@scope/name" → "*" entries.
|
|
// Only MCPs with a scope directory (mcps/{scope}/) are included; top-level mcps/ are skipped.
|
|
func ScanDependencies(assistantDir, appRoot string) (map[string]string, error) {
|
|
pkgPath := filepath.Join(assistantDir, "package.yao")
|
|
data, err := os.ReadFile(pkgPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read package.yao: %w", err)
|
|
}
|
|
|
|
var pkg packageYao
|
|
if err := goujson.ParseFile("package.yao", data, &pkg); err != nil {
|
|
return nil, fmt.Errorf("parse package.yao: %w", err)
|
|
}
|
|
|
|
deps := map[string]string{}
|
|
|
|
// Scan MCP servers
|
|
if pkg.MCP != nil {
|
|
for _, entry := range pkg.MCP.Servers {
|
|
serverID := entry.ServerID
|
|
if serverID == "" {
|
|
continue
|
|
}
|
|
pkgID, err := resolveMCPDep(serverID, appRoot)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if pkgID != "" {
|
|
deps[pkgID] = "*"
|
|
}
|
|
}
|
|
}
|
|
|
|
return deps, nil
|
|
}
|
|
|
|
// resolveMCPDep resolves an MCP server_id to a package ID if it lives under a scoped directory.
|
|
// Returns empty string for non-scoped (local) MCPs.
|
|
func resolveMCPDep(serverID, appRoot string) (string, error) {
|
|
// server_id like "yao.rag-tools" → scope=yao, name=rag-tools
|
|
// Check if mcps/yao/rag-tools/ exists (has scope directory)
|
|
scope, name, err := common.IDFromYaoID(serverID)
|
|
if err != nil {
|
|
return "", nil
|
|
}
|
|
|
|
mcpDir := filepath.Join(appRoot, "mcps", scope, strings.ReplaceAll(name, ".", "/"))
|
|
if _, err := os.Stat(mcpDir); err == nil {
|
|
return common.FormatPackageID(scope, name), nil
|
|
}
|
|
|
|
// Also check for single-file MCP: mcps/{scope}/{name}.mcp.yao
|
|
// This is less common but possible
|
|
mcpFile := filepath.Join(appRoot, "mcps", scope, name+".mcp.yao")
|
|
if _, err := os.Stat(mcpFile); err == nil {
|
|
return common.FormatPackageID(scope, name), nil
|
|
}
|
|
|
|
return "", nil
|
|
}
|