返回 DeepSeek-Reasonix
project_mcp_notice.go
根目录 / internal / boot / project_mcp_notice.go
1 package boot
2
3 import (
4 "fmt"
5 "strings"
6 "sync"
7
8 "reasonix/internal/config"
9 "reasonix/internal/event"
10 )
11
12 // projectMCPNoticesShown keeps the notice to once per process for each
13 // declaration, so rebuilding a controller does not repeat it.
14 var projectMCPNoticesShown sync.Map
15
16 // emitProjectMCPDecisionNotice tells the user which project-declared servers are
17 // off pending their decision, with what each would run.
18 func emitProjectMCPDecisionNotice(sink event.Sink, cfg *config.Config, root string) {
19 if sink == nil || cfg == nil {
20 return
21 }
22 var lines []string
23 for _, p := range cfg.Plugins {
24 d := config.MCPServerDecision(p, root)
25 if !d.AwaitsUser() {
26 continue
27 }
28 key := strings.Join([]string{config.ReasonixHomeDir(), root, p.Name, d.Code(), config.MCPLaunchLine(p)}, "\x00")
29 if _, seen := projectMCPNoticesShown.LoadOrStore(key, true); seen {
30 continue
31 }
32 lines = append(lines, fmt.Sprintf(" %s [%s]: %s", p.Name, d.Code(), config.MCPLaunchLine(p)))
33 }
34 if len(lines) == 0 {
35 return
36 }
37 sink.Emit(event.Event{
38 Kind: event.Notice, Level: event.LevelWarn,
39 Text: "This project declares MCP servers that stay off until you enable them.",
40 Detail: strings.Join(lines, "\n") +
41 "\nReview each one (full declaration: `reasonix mcp get <name>`), then enable it with `reasonix mcp enable <name>`, /mcp connect, or the MCP panel.",
42 })
43 }
44
44 lines GO