✨ feat(kebab-cli): kebab mcp subcommand (fb-30)

Wires kebab_mcp::serve_stdio into kebab-cli. `--config <path>` honored
via the established Config::load pattern.

Updated serve_stdio signature to (Config, Option<PathBuf>) so the doctor
tool's path-aware behavior works correctly via KebabAppState.

Smoke test spawns the binary + sends initialize + initialized +
tools/list over stdin, asserts 4 tools returned. Confirms the MCP
server boots end-to-end via the real binary (rmcp 1.6 has no
in-memory test transport, so this is the only end-to-end assertion).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
th-kim0823
2026-05-07 16:10:17 +09:00
parent 61eef9bc82
commit 4a30959fdd
5 changed files with 101 additions and 4 deletions

View File

@@ -4,6 +4,8 @@
//!
//! See spec `docs/superpowers/specs/2026-05-07-p9-fb-30-mcp-server-design.md`.
use std::path::PathBuf;
use anyhow::Result;
use rmcp::ServerHandler;
@@ -142,16 +144,21 @@ impl ServerHandler for KebabHandler {
/// Run the MCP server on stdio JSON-RPC. Blocks until the client closes
/// the stream (typically when the agent host exits).
pub fn serve_stdio(cfg: Config) -> Result<()> {
///
/// `config_path` is the path passed via `--config <path>`, if any.
/// It is forwarded to `KebabAppState` so the doctor tool can honour the
/// same config file the server was started with (falls back to XDG default
/// when `None`).
pub fn serve_stdio(cfg: Config, config_path: Option<PathBuf>) -> Result<()> {
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?;
runtime.block_on(serve_stdio_async(cfg))
runtime.block_on(serve_stdio_async(cfg, config_path))
}
async fn serve_stdio_async(cfg: Config) -> Result<()> {
async fn serve_stdio_async(cfg: Config, config_path: Option<PathBuf>) -> Result<()> {
tracing::info!("kebab-mcp: starting stdio server");
let state = KebabAppState::new(cfg, None); // Plan Task 10 will thread the actual path
let state = KebabAppState::new(cfg, config_path);
let handler = KebabHandler::new(state);
let service = handler.serve(stdio()).await?;
service.waiting().await?;