supercode-cli 0.4.20

supercode — a lightweight, fully-customizable AI coding agent CLI in Rust. Any model via OpenRouter; natively continues Claude Code and Codex sessions.
//! §2 module 15 `mcp.client`: CLI-level proof of exactly when
//! `[capabilities.mcp.servers.*]` is consulted — spawns the real built
//! `supercode` binary, same idiom as `mcp_import_cli.rs`/`mcp_oauth_cli.rs`.
//!
//! BP-1 moved the gate: `capabilities.mcp.enabled = true` in the USER
//! config layer is now the WHOLE answer (`mcp_security.rs` already proves a
//! PROJECT layer can't set it at all). It used to also require
//! `[experimental] module_registry = true`, so the config a user would
//! reasonably write — just `[capabilities.mcp] enabled = true` — silently
//! did nothing. `[experimental] module_registry = false` is the explicit
//! opt-out and still shuts the whole module off.
//!
//! Uses `command = "false"` (a real binary every POSIX system has, that
//! exits immediately doing nothing) as the "MCP server" — not a real MCP
//! server, just a process whose CONNECTION ATTEMPT is externally observable
//! via the `mcp: `probe` connect failed` stderr line, which is exactly the
//! signal these tests check for (present = attempted the entry at all;
//! absent = the module gate correctly skipped it).

use std::path::{Path, PathBuf};
use std::process::{Command, Output};

fn bin() -> PathBuf {
    PathBuf::from(env!("CARGO_BIN_EXE_supercode"))
}

fn fresh_dir(tag: &str) -> PathBuf {
    let nanos = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_nanos();
    let dir = std::env::temp_dir().join(format!(
        "supercode-mcpgate-{tag}-{}-{nanos}",
        std::process::id()
    ));
    std::fs::create_dir_all(&dir).unwrap();
    dir
}

fn run(supercode_home: &Path, project_dir: &Path) -> Output {
    Command::new(bin())
        .current_dir(project_dir)
        .env("SUPERCODE_HOME", supercode_home)
        .env("HOME", supercode_home)
        // A deliberately bogus key: attach_mcp runs BEFORE the (doomed)
        // model call, so the mcp: connect-attempt stderr line is already
        // written by the time the run fails on auth — never a real
        // network call to a real provider succeeding/spending anything.
        .env("OPENROUTER_API_KEY", "dummy-key-never-valid")
        .args(["run", "hi", "--dangerous"])
        .stdin(std::process::Stdio::null())
        .stdout(std::process::Stdio::piped())
        .stderr(std::process::Stdio::piped())
        .output()
        .expect("failed to spawn the supercode binary")
}

fn stderr(out: &Output) -> String {
    String::from_utf8_lossy(&out.stderr).into_owned()
}

const PROBE_SERVER_TOML: &str = r#"
[capabilities.mcp.servers.probe]
command = "false"
args = []
"#;

/// The module's own bit is off (a `[capabilities.mcp.servers.*]` table
/// alone never sets `enabled`), so the entry is never attempted.
#[test]
fn module_off_by_default_never_attempts_a_capabilities_mcp_servers_entry() {
    let home = fresh_dir("off");
    let project = fresh_dir("off-project");
    std::fs::write(
        home.join("config.toml"),
        format!("schema_version = 1\n{PROBE_SERVER_TOML}"),
    )
    .unwrap();

    let out = run(&home, &project);
    let err = stderr(&out);
    assert!(
        !err.contains("probe"),
        "capabilities.mcp.servers.probe must never be attempted with the mcp module off: {err}"
    );

    std::fs::remove_dir_all(&home).ok();
    std::fs::remove_dir_all(&project).ok();
}

/// BP-1: `capabilities.mcp.enabled = true` ALONE — no `[experimental]`
/// table anywhere — is the config a user would reasonably write expecting
/// it to just work, and it now does.
#[test]
fn mcp_enabled_true_alone_attempts_the_capabilities_mcp_servers_entry() {
    let home = fresh_dir("on");
    let project = fresh_dir("on-project");
    std::fs::write(
        home.join("config.toml"),
        format!("schema_version = 1\n[capabilities.mcp]\nenabled = true\n{PROBE_SERVER_TOML}"),
    )
    .unwrap();

    let out = run(&home, &project);
    let err = stderr(&out);
    assert!(
        err.contains("probe"),
        "with capabilities.mcp.enabled on, the capabilities.mcp.servers entry \
         must be attempted: {err}"
    );

    std::fs::remove_dir_all(&home).ok();
    std::fs::remove_dir_all(&project).ok();
}

/// The explicit opt-out is load-bearing: `[experimental] module_registry =
/// false` puts the whole module resolution back on the legacy path, so even
/// `capabilities.mcp.enabled = true` is not consulted.
#[test]
fn module_registry_false_opts_out_of_the_capabilities_mcp_servers_entry() {
    let home = fresh_dir("optout");
    let project = fresh_dir("optout-project");
    std::fs::write(
        home.join("config.toml"),
        format!(
            "schema_version = 1\n[experimental]\nmodule_registry = false\n\
             [capabilities.mcp]\nenabled = true\n{PROBE_SERVER_TOML}"
        ),
    )
    .unwrap();

    let out = run(&home, &project);
    let err = stderr(&out);
    assert!(
        !err.contains("probe"),
        "module_registry = false must skip the capabilities.mcp.servers entry: {err}"
    );

    std::fs::remove_dir_all(&home).ok();
    std::fs::remove_dir_all(&project).ok();
}