feat(kebab-mcp): handler skeleton + initialize handshake (fb-30)

KebabHandler implements rmcp::ServerHandler::get_info — returns
serverInfo (name="kebab", version from CARGO_PKG_VERSION) and
capabilities.tools. KebabAppState wraps Config in Arc for cheap clone
into per-request task scope. serve_stdio entry builds a multi-thread
tokio runtime and runs the server until client closes the stream.

rmcp 1.6 API used:
- rmcp::ServerHandler trait (re-exported from handler::server)
- ServerInfo::new(caps).with_server_info(impl) builder (not struct-init:
  InitializeResult/Implementation are #[non_exhaustive])
- ServerCapabilities::builder().enable_tools().build() — builder macro
  generated, confirms the plan-literal pattern works
- Implementation::new(name, version) — non-exhaustive constructor
- rmcp::transport::stdio() returns (tokio::io::Stdin, tokio::io::Stdout)
  tuple; tuple impls IntoTransport via AsyncRead+AsyncWrite blanket
- handler.serve(transport).await → RunningService<RoleServer, H>
  (ServiceExt::serve, returns Result<_, ServerInitializeError>)
- service.waiting().await → Result<QuitReason, JoinError>
- serve_stdio is plain fn wrapping a manually-built tokio runtime
  (avoids nested-runtime hazard if kebab-cli ever gains its own rt)

Tools wire-up lands in subsequent tasks (one tool per task).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
th-kim0823
2026-05-07 15:31:13 +09:00
parent 2c09ed6af4
commit 8f6e6bc01a
3 changed files with 84 additions and 3 deletions

View File

@@ -0,0 +1,19 @@
//! Integration: KebabHandler::get_info returns correct kebab serverInfo.
//! Doesn't exercise full transport — that lands when we have at least
//! one tool to call (Task 4+).
use kebab_config::Config;
use kebab_mcp::{KebabAppState, KebabHandler};
use rmcp::ServerHandler;
#[tokio::test]
async fn initialize_returns_kebab_server_info() {
let cfg = Config::defaults();
let state = KebabAppState::new(cfg);
let handler = KebabHandler::new(state);
let info = handler.get_info();
assert_eq!(info.server_info.name, "kebab");
assert!(!info.server_info.version.is_empty());
assert!(info.capabilities.tools.is_some());
}