Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ cargo run -p decodex --bin decodex -- intake goal --project decodex <CONTRACT_ID
cargo run -p decodex --bin decodex -- intake goal --project decodex <CONTRACT_ID> --apply
cargo run -p decodex --bin decodex -- intake issues --project decodex XY-1 XY-2 --dry-run
cargo run -p decodex --bin decodex -- intake issues --project decodex XY-1 XY-2 --apply
cargo run -p decodex --bin decodex -- mcp serve --transport stdio
cargo run -p decodex --bin decodex -- radar refresh-upstream-queue
cargo run -p decodex --bin decodex -- radar refresh-release-delta
cargo run -p decodex --bin decodex -- radar validate
Expand Down Expand Up @@ -185,6 +186,12 @@ Program, and issue mappings. It never applies or removes service queue labels; r
mapped nodes are dispatched directly by the Program scheduler instead of being
converted into queued-label work.

`decodex mcp serve --transport stdio` starts the phase-one read-only MCP gateway for
local desktop and CLI clients. The gateway exposes checked-in documentation, checked-in
JSON research reports, runtime Decision Contract readback, local status snapshots, and
lane-control readback as MCP resources only. It does not expose mutating MCP tools.
Stdout is reserved for MCP JSON-RPC messages; diagnostics and logs stay off stdout.

### Install from Source

```sh
Expand Down
99 changes: 83 additions & 16 deletions apps/decodex/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::{
archive_hygiene::{self, ArchiveHygieneRequest},
maintenance::{self, MaintenanceMode, MaintenancePruneRequest, MaintenanceScope},
manual::{self, ManualCommitRequest, ManualLandRequest},
mcp::{self, McpServeRequest, McpTransport},
orchestrator::{
self, DEFAULT_STEER_RESULT_WAIT_TIMEOUT, DiagnoseRequest, EvidenceRequest,
IssueDispatchMode, LaneInspectRequest, LaneInterruptRequest, LaneSteerReport,
Expand Down Expand Up @@ -73,6 +74,7 @@ impl Cli {
Command::Land(args) => args.run(),
Command::Run(args) => args.run(),
Command::Serve(args) => args.run(),
Command::Mcp(args) => args.run(),
Command::Project(args) => args.run(),
Command::Lane(args) => args.run(),
Command::Status(args) => args.run(),
Expand Down Expand Up @@ -368,6 +370,36 @@ impl ServeCommand {
}
}

#[derive(Debug, Args)]
struct McpCommand {
#[command(subcommand)]
command: McpSubcommand,
}
impl McpCommand {
fn run(&self) -> Result<()> {
match &self.command {
McpSubcommand::Serve(args) => args.run(),
}
}
}

#[derive(Debug, Args)]
struct McpServeCommand {
#[command(flatten)]
project_config: ProjectConfigArgs,
/// MCP transport.
#[arg(long, value_enum, default_value_t = McpTransport::Stdio)]
transport: McpTransport,
}
impl McpServeCommand {
fn run(&self) -> Result<()> {
mcp::serve(McpServeRequest {
transport: self.transport,
config_path: self.project_config.as_path(),
})
}
}

#[derive(Debug, Args)]
struct ProjectCommand {
#[command(subcommand)]
Expand Down Expand Up @@ -1634,6 +1666,8 @@ enum Command {
Run(RunCommand),
/// Run the local multi-project Decodex control plane.
Serve(ServeCommand),
/// Serve the read-only Decodex MCP gateway.
Mcp(McpCommand),
/// Manage the local Decodex project registry.
Project(ProjectCommand),
/// Inspect or influence a local lane.
Expand Down Expand Up @@ -1697,6 +1731,12 @@ enum ProjectSubcommand {
Remove(ProjectToggleCommand),
}

#[derive(Debug, Subcommand)]
enum McpSubcommand {
/// Serve read-only Decodex resources over MCP.
Serve(McpServeCommand),
}

#[derive(Debug, Subcommand)]
enum LaneSubcommand {
/// Inspect one local lane by issue identifier or tracker issue id.
Expand Down Expand Up @@ -1929,22 +1969,26 @@ mod tests {

use clap::Parser;

use crate::cli::{
AccountCommand, AccountSubcommand, AccountUseCommand, AppCommand, AttemptCommand, Cli,
Command, CommitCommand, DiagnoseCommand, EvidenceCommand, IntakeCommand, IntakeGoalCommand,
IntakeIssuesCommand, IntakeSubcommand, LandCommand, LaneCommand, LaneInspectCommand,
LaneInterruptCommand, LaneSteerCommand, LaneSubcommand, LegacyCloseoutRecoveryCommand,
MergedCloseoutRecoveryCommand, ProbeCommand, ProjectCommand, ProjectConfigArgs,
ProjectSubcommand, RadarBackfillReleaseRangeCommand, RadarBundleBuildCommand,
RadarBundleCommand, RadarBundleSubcommand, RadarBundleValidateCommand, RadarCommand,
RadarLedgerCommand, RadarLedgerIngestExistingCommand, RadarLedgerSubcommand,
RadarLedgerSummaryCommand, RadarRefreshReleaseDeltaCommand,
RadarRefreshUpstreamQueueCommand, RadarRenderSignalCommand, RadarSubcommand,
RadarValidateCommand, RecoverCommand, RecoverSubcommand, ResearchCommand,
ResearchCompileCommand, ResearchOutcomeArg, ResearchPromoteCommand, ResearchSubcommand,
ReviewHandoffAdoptCommand, ReviewHandoffDiagnoseCommand, ReviewHandoffRebindCommand,
ReviewHandoffRecoveryCommand, ReviewHandoffRecoverySubcommand, RunCommand, ServeCommand,
StatusCommand,
use crate::{
cli::{
AccountCommand, AccountSubcommand, AccountUseCommand, AppCommand, AttemptCommand, Cli,
Command, CommitCommand, DiagnoseCommand, EvidenceCommand, IntakeCommand,
IntakeGoalCommand, IntakeIssuesCommand, IntakeSubcommand, LandCommand, LaneCommand,
LaneInspectCommand, LaneInterruptCommand, LaneSteerCommand, LaneSubcommand,
LegacyCloseoutRecoveryCommand, McpCommand, McpServeCommand, McpSubcommand,
MergedCloseoutRecoveryCommand, ProbeCommand, ProjectCommand, ProjectConfigArgs,
ProjectSubcommand, RadarBackfillReleaseRangeCommand, RadarBundleBuildCommand,
RadarBundleCommand, RadarBundleSubcommand, RadarBundleValidateCommand, RadarCommand,
RadarLedgerCommand, RadarLedgerIngestExistingCommand, RadarLedgerSubcommand,
RadarLedgerSummaryCommand, RadarRefreshReleaseDeltaCommand,
RadarRefreshUpstreamQueueCommand, RadarRenderSignalCommand, RadarSubcommand,
RadarValidateCommand, RecoverCommand, RecoverSubcommand, ResearchCommand,
ResearchCompileCommand, ResearchOutcomeArg, ResearchPromoteCommand, ResearchSubcommand,
ReviewHandoffAdoptCommand, ReviewHandoffDiagnoseCommand, ReviewHandoffRebindCommand,
ReviewHandoffRecoveryCommand, ReviewHandoffRecoverySubcommand, RunCommand,
ServeCommand, StatusCommand,
},
mcp::McpTransport,
};

#[test]
Expand Down Expand Up @@ -2191,6 +2235,29 @@ mod tests {
}
}

#[test]
fn parses_mcp_stdio_serve() {
let cli = Cli::parse_from([
"decodex",
"mcp",
"serve",
"--config",
"./project.toml",
"--transport",
"stdio",
]);

assert!(matches!(
cli.command,
Command::Mcp(McpCommand {
command: McpSubcommand::Serve(McpServeCommand {
project_config: ProjectConfigArgs { config: Some(config) },
transport: McpTransport::Stdio,
})
}) if config == Path::new("./project.toml")
));
}

#[test]
fn parses_radar_validate_paths() {
let cli = Cli::parse_from(["decodex", "radar", "validate", "artifacts/github/bundles"]);
Expand Down
1 change: 1 addition & 0 deletions apps/decodex/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod github;
mod loop_contract;
mod maintenance;
mod manual;
mod mcp;
mod orchestrator;
mod program_intake;
mod pull_request;
Expand Down
Loading