Skip to content

Commit e77d80c

Browse files
authored
[P0] cortex mcp-server --verify (CLI audit P0-2) (#52)
* feat(cli): add hidden mcp-server --verify Offline stdio JSON-RPC verifier (cortex-verify/1) for TUI chrome, lock scenes, login product copy, and API error paths. Hidden until Designer sign-off. CLI_100_AUDIT_READY + CLI_100_CHROME_LOCK_SIGNED. Co-authored-by: Mathis <echobt@users.noreply.github.com> * fix(cli): green verify mcp source policy and coverage Extract mcp-server dispatch from oversized modules, regenerate the CLI schema, and add unit tests for the hidden verify paths. mcp-server stays hide=true. Co-authored-by: Mathis <echobt@users.noreply.github.com> --------- Co-authored-by: Mathis <echobt@users.noreply.github.com>
1 parent 8fa4954 commit e77d80c

28 files changed

Lines changed: 2817 additions & 128 deletions

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
## Unreleased
1111

12+
### Added
13+
- Hidden `cortex mcp-server --verify` stdio JSON-RPC server (`cortex-verify`) so CI and agents can audit TUI chrome, lock scenes, login product copy, and API error paths offline. Remains `hide = true` until Designer sign-off.
14+
1215
### Changed
1316
- Code turns default to the **Cloud** runtime for the TUI and `cortex exec` (Designer Q9 / `CLI_100_CHROME_LOCK_SIGNED`). This PC and SSH are explicit opt-in in 0.1.x (`CORTEX_COMPUTER` or `CORTEX_SSH_HOST`) and refuse a fresh session with product copy instead of blocking every first turn.
1417
- README `docs/media/intro.gif` sits on a photographed green forest desktop (not teal blobs): Terminal chrome, a pointer that walks titlebar → composer → slash / model → Shell, and the signed lock TUI. Local CLI only — no Cortex Cloud handoff in the banner story.

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/guides/development.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,26 @@ failure to make CI green. See [testing rules](../../.rules/testing.md).
9494
The append regression test checks immediate visibility after Tokio 1.53.1 file
9595
writes. An awaited `flush` finishes the pending write; it is not an `fsync`
9696
durability guarantee. Do not replace this check with sleeps or retries.
97+
98+
## Verification MCP (hidden)
99+
100+
`cortex mcp-server --verify` is a hidden stdio JSON-RPC server (`hide = true`
101+
until Designer sign-off). It is built on the in-tree `cortex-mcp-server` crate
102+
as `cortex-verify` and drives the TUI through the same headless
103+
`EventLoop` + `MockTerminal` path as `ux_contract_tests.rs`.
104+
105+
CI and agents add one MCP server entry:
106+
107+
```bash
108+
./target/debug/Cortex mcp-server --verify
109+
```
110+
111+
Tools: `tui.*`, `lock.*`, `login.run`, `api.*`, `mcp.*`, `report.finish`.
112+
Resources: `cortex-verify://matrix`, `cortex-verify://lock/v2/<size>/<id>.txt`,
113+
`cortex-verify://report/latest`. `report.finish` writes
114+
`target/readiness/cli-verify/<run_id>.json` with schema `cortex-verify/1`.
115+
116+
Offline runs use `CORTEX_API_URL` (loopback fixture or an unreachable origin).
117+
They must still cover chrome, legend, product-facing errors, and palette
118+
audit. Live API checks are gated on `CORTEX_LIVE_API=1` and are not part of
119+
default CI. Integration coverage is `src/cortex-cli/tests/mcp_server_verify.rs`.

docs/reference/cli.commands.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2744,6 +2744,13 @@
27442744
{
27452745
"about": "Run the MCP server (stdio transport)",
27462746
"arguments": [
2747+
{
2748+
"help": "Run the Cortex verification MCP over stdio JSON-RPC (`cortex-verify/1`)",
2749+
"id": "verify",
2750+
"long": "verify",
2751+
"required": false,
2752+
"short": null
2753+
},
27472754
{
27482755
"help": "Enable verbose output (same as --log-level debug)",
27492756
"id": "verbose",

docs/reference/cli.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,9 @@ help but supported.
257257

258258
Also hidden, and supported: `shell` (aliases `interactive`, `repl`), `dag`
259259
(alias `tasks`), `servers`, `history`, `workspace` (alias `project`), `sandbox`
260-
(alias `sb`), `serve`, and `mcp-server`.
260+
(alias `sb`), `serve`, and `mcp-server`. The hidden `cortex mcp-server --verify`
261+
flag starts the offline TUI+API verification MCP (`cortex-verify/1`); see
262+
[Development](../guides/development.md#verification-mcp-hidden).
261263

262264
## See also
263265

src/cortex-cli/Cargo.toml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,16 @@ workspace = true
2424
[features]
2525
default = ["cortex-tui", "audio"]
2626
# Use the new Cortex TUI (120 FPS, Cortex theme)
27-
cortex-tui = ["dep:cortex-tui"]
27+
cortex-tui = [
28+
"dep:cortex-tui",
29+
"dep:cortex-tui-capture",
30+
"dep:cortex-mcp-server",
31+
"dep:cortex-mcp-types",
32+
"dep:sha2",
33+
"dep:hex",
34+
"dep:ratatui",
35+
"dep:async-trait",
36+
]
2837
# Audio notifications - disabled on musl targets due to alsa-sys incompatibility
2938
# Falls back to terminal bell when disabled
3039
audio = ["cortex-tui?/audio"]
@@ -33,6 +42,13 @@ audio = ["cortex-tui?/audio"]
3342
cortex-engine = { workspace = true }
3443
cortex-protocol = { workspace = true }
3544
cortex-tui = { workspace = true, optional = true }
45+
cortex-tui-capture = { workspace = true, optional = true }
46+
cortex-mcp-server = { workspace = true, optional = true }
47+
cortex-mcp-types = { workspace = true, optional = true }
48+
sha2 = { workspace = true, optional = true }
49+
hex = { workspace = true, optional = true }
50+
ratatui = { workspace = true, optional = true }
51+
async-trait = { workspace = true, optional = true }
3652

3753
cortex-common = { workspace = true, features = ["cli"] }
3854
cortex-commands = { workspace = true }

src/cortex-cli/src/cli/args.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ pub enum Commands {
366366
/// Run the MCP server (stdio transport)
367367
#[command(display_order = 32, hide = true)]
368368
#[command(next_help_heading = categories::EXTENSION)]
369-
McpServer,
369+
McpServer(super::mcp_server::McpServerCli),
370370

371371
/// Start ACP server for IDE integration (e.g., Zed)
372372
#[command(display_order = 33)]

src/cortex-cli/src/cli/handlers.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ pub async fn dispatch_command(cli: Cli) -> Result<()> {
3232
}
3333
Some(Commands::Mcp(mcp_cli)) => mcp_cli.run().await,
3434
Some(Commands::Agent(agent_cli)) => agent_cli.run().await,
35-
Some(Commands::McpServer) => {
36-
bail!(
37-
"MCP server mode is not yet implemented. Use 'cortex mcp' for MCP server management."
38-
);
39-
}
35+
Some(Commands::McpServer(args)) => super::mcp_server::run(args).await,
4036
Some(Commands::Completion(completion_cli)) => handle_completion(completion_cli),
4137
Some(Commands::Sandbox(sandbox_args)) => handle_sandbox(sandbox_args).await,
4238
Some(Commands::Resume(resume_cli)) => run_resume(resume_cli).await,
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//! Hidden `cortex mcp-server` flags and dispatch.
2+
//!
3+
//! Kept out of [`super::args`] / [`super::handlers`] so those modules stay at
4+
//! their source-policy line-count baseline (same split as `lock_palette`).
5+
6+
use anyhow::{Result, bail};
7+
use clap::Parser;
8+
9+
/// Hidden `cortex mcp-server` flags. `--verify` is the offline TUI+API verifier.
10+
#[derive(Debug, Parser)]
11+
pub struct McpServerCli {
12+
/// Run the Cortex verification MCP over stdio JSON-RPC (`cortex-verify/1`).
13+
#[arg(long)]
14+
pub verify: bool,
15+
}
16+
17+
/// Run `cortex mcp-server`, including the hidden `--verify` verifier.
18+
pub async fn run(args: McpServerCli) -> Result<()> {
19+
if args.verify {
20+
#[cfg(feature = "cortex-tui")]
21+
{
22+
return crate::verify_mcp::run().await;
23+
}
24+
#[cfg(not(feature = "cortex-tui"))]
25+
{
26+
bail!("Verification MCP requires the cortex-tui feature.");
27+
}
28+
}
29+
bail!("MCP server mode is not yet implemented. Use 'cortex mcp' for MCP server management.");
30+
}
31+
32+
#[cfg(test)]
33+
mod tests {
34+
use super::*;
35+
use crate::cli::args::{Cli, Commands};
36+
use clap::CommandFactory;
37+
38+
#[test]
39+
fn test_mcp_server_verify_stays_hidden() {
40+
let command = Cli::command();
41+
let mcp = command
42+
.find_subcommand("mcp-server")
43+
.expect("mcp-server must exist");
44+
assert!(mcp.is_hide_set(), "keep hide=true until Designer sign-off");
45+
let cli = Cli::try_parse_from(["cortex", "mcp-server", "--verify"])
46+
.expect("should parse hidden mcp-server --verify");
47+
match cli.command {
48+
Some(Commands::McpServer(args)) => assert!(args.verify),
49+
_ => panic!("expected McpServer --verify"),
50+
}
51+
}
52+
53+
#[tokio::test]
54+
async fn mcp_server_without_verify_fails_closed() {
55+
let err = run(McpServerCli { verify: false })
56+
.await
57+
.expect_err("default mcp-server is not implemented");
58+
assert!(err.to_string().contains("not yet implemented"));
59+
}
60+
61+
#[tokio::test]
62+
async fn dispatch_mcp_server_without_verify_fails_closed() {
63+
let cli = Cli::try_parse_from(["cortex", "mcp-server"]).expect("parse mcp-server");
64+
let err = crate::cli::handlers::dispatch_command(cli)
65+
.await
66+
.expect_err("dispatch must fail closed");
67+
assert!(err.to_string().contains("not yet implemented"));
68+
}
69+
}

src/cortex-cli/src/cli/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
1414
pub mod args;
1515
pub mod handlers;
16+
pub mod mcp_server;
1617
pub mod styles;
1718

1819
// Re-export main types

0 commit comments

Comments
 (0)