package main import ( "context" "encoding/json" "strings" "time" "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/mcp" "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/registry" "git.yeij.top/AskaEth/Cyrene_v2/internal/config" "git.yeij.top/AskaEth/Cyrene_v2/internal/logger" ) // mcpConnectTimeout bounds each MCP server's connect+discover handshake so a hung // server degrades to a warning instead of blocking startup. const mcpConnectTimeout = 15 * time.Second // wireMCP reads cfg.MCP.ServersJSON, connects to every enabled server, registers // its discovered tools into the capability registry (KindTool so the agent can // call them), and surfaces a KindMCP capability so the admin plugin list // enumerates the server. A server that fails to connect or is unconfigured // degrades to a warning and never blocks startup. func (a *app) wireMCP() { log := logger.WithSource(a.log, "cmd/cyrene") servers, err := a.mcpServers() if err != nil { log.Warn("mcp config parse skipped", "error", err) return } if len(servers) == 0 { log.Debug("no mcp servers configured") return } for _, srv := range servers { if !srv.Enabled { continue } ctx, cancel := context.WithTimeout(context.Background(), mcpConnectTimeout) err := a.registerMCPServer(ctx, srv) cancel() if err != nil { log.Warn("mcp server skipped", "name", srv.Name, "error", err) } } } // mcpServers parses the configured MCP servers from cfg.MCP.ServersJSON. func (a *app) mcpServers() ([]config.MCPServer, error) { raw := strings.TrimSpace(a.cfg.MCP.ServersJSON) if raw == "" { return nil, nil } var servers []config.MCPServer if err := json.Unmarshal([]byte(raw), &servers); err != nil { return nil, err } return servers, nil } // registerMCPServer connects to one server, registers its tools, and publishes a // KindMCP capability under "mcp:". It returns the first error so callers // can classify it; on partial registration it cleans up what it added. func (a *app) registerMCPServer(ctx context.Context, srv config.MCPServer) error { log := logger.WithSource(a.log, "cmd/cyrene") reg := mcp.NewRegistry(a.caps, mcp.MCPConfig{ Name: srv.Name, Command: srv.Command, Args: srv.Args, URL: srv.URL, }, mcp.WithLogger(a.log)) if err := reg.Register(ctx); err != nil { _ = reg.Unregister(context.Background()) return err } if err := a.caps.Register(registry.Capability{ Name: "mcp:" + srv.Name, Kind: registry.KindMCP, Description: "MCP server " + srv.Name, Value: reg, }); err != nil { log.Warn("mcp capability register skipped", "name", srv.Name, "error", err) } a.mcpRegs = append(a.mcpRegs, reg) log.Info("mcp server registered", "name", srv.Name) return nil }