package main import ( "context" "strings" "testing" "github.com/kami/maven/internal/config" ) func TestWireMCPOffWhenUnconfigured(t *testing.T) { st := newTestStore(t) for name, cfg := range map[string]*config.Config{ "no block": {}, "nothing enabled": {MCP: &config.MCPConfig{Servers: []config.MCPServerConfig{ {Name: "vikunja", URL: "http://192.168.1.104:9100/mcp"}, }}}, } { t.Run(name, func(t *testing.T) { if w := wireMCP(cfg, st); w != nil { t.Fatal("MCP must be off unless a server is configured AND enabled") } }) } // nil wiring must be safe to use everywhere it is reachable. var w *mcpWiring w.close() w.propose(context.Background()) if w.status() != nil || w.caller() != nil { t.Fatal("a nil wiring must report nothing") } } // Wiring must not dial. Boot used to block for the whole per-server timeout // budget on a black-holed endpoint, and on the passkey path that delay landed // inside the unlock handler. func TestWireMCPDoesNotDial(t *testing.T) { st := newTestStore(t) w := wireMCP(&config.Config{MCP: &config.MCPConfig{Servers: []config.MCPServerConfig{{ Name: "dead", Command: "/nonexistent/mcp-server", Enabled: true, }}}}, st) if w == nil { t.Fatal("a configured server should wire") } defer w.close() if s := w.status(); len(s) != 1 || s[0].Err != "" { t.Fatalf("wireMCP dialled: %+v", s) } } // An unreachable server must not stop the daemon, must be reported as down, and // must propose nothing. func TestWireMCPUnreachableServerIsNotFatal(t *testing.T) { st := newTestStore(t) w := wireMCP(&config.Config{MCP: &config.MCPConfig{Servers: []config.MCPServerConfig{{ Name: "dead", Command: "/nonexistent/mcp-server", Enabled: true, }}}}, st) if w == nil { t.Fatal("a configured server should still wire") } defer w.close() w.connect(context.Background()) st2 := w.status() if len(st2) != 1 || st2[0].Connected || st2[0].Err == "" { t.Fatalf("status = %+v", st2) } tools, err := st.ListTools(context.Background(), "") if err != nil { t.Fatal(err) } if len(tools) != 0 { t.Fatalf("a server that never answered must propose nothing, got %+v", tools) } } // A url server whose address is private is refused by webfetch unless that // server sets allow_private. This is the guard the whole MCP path rides on, so // it is asserted here too, at the wiring level. func TestWireMCPPrivateURLRefusedWithoutAllowPrivate(t *testing.T) { st := newTestStore(t) w := wireMCP(&config.Config{MCP: &config.MCPConfig{Servers: []config.MCPServerConfig{{ Name: "lan", URL: "http://127.0.0.1:9100/mcp", Enabled: true, }}}}, st) if w == nil { t.Fatal("should wire") } defer w.close() w.connect(context.Background()) s := w.status()[0] if s.Connected { t.Fatal("a loopback server must not connect without allow_private") } if !strings.Contains(s.Err, "private address") { t.Fatalf("err = %q, want the private-address refusal", s.Err) } }