Skip to main content

Module server

Module server 

Source
Expand description

MCP stdio server (lands across Tasks 12-15 of the M1a “Crucible” plan).

PINNED rmcp API (version 3.1.4) — reused by tools.rs / server.rs:

  • server trait: rmcp::ServerHandler
  • tool declaration: #[rmcp::tool_router] on the impl block (generates Self::tool_router()), #[rmcp::tool(description = “…”)] on each async method, #[rmcp::tool_handler] on the impl ServerHandler for T block (wires call_tool/list_tools) — the fully-qualified attribute paths still resolve: rmcp_macros is re-exported at the crate root via pub use rmcp_macros::*; (rmcp-3.1.4/src/lib.rs)
  • server info: rmcp::model::ServerConfig (a #[non_exhaustive] type alias for InitializeResult as of 3.1 — no more struct-literal + ..Default::default() from outside the defining crate), built via the builder methods instead: ServerConfig::new(capabilities).with_instructions(text); capabilities still come from rmcp::model::ServerCapabilities::builder().enable_tools().build()
  • tool result OK: rmcp::model::CallToolResult::success(vec![ContentBlock::text(s)])
  • tool result domain-err: rmcp::model::CallToolResult::error(vec![ContentBlock::text(s)]) // isError:true
  • text content: rmcp::model::ContentBlock::text(String) — the flat content union was renamed from Content (0.8) to ContentBlock (3.1); CallToolResult::success/error take Vec<ContentBlock> accordingly (rmcp-3.1.4/src/model/content.rs, rmcp-3.1.4/src/model.rs around the CallToolResult definition)
  • structured content: rmcp::model::CallToolResult::structured(serde_json::Value) (success, sets structured_content + mirrors it as text) and CallToolResult::structured_error(Value) for the error case; CallToolResult also exposes into_typed::<T>() to deserialize back
  • schema-invalid params: the #[tool] macro’s generated argument parsing returns rmcp::ErrorData::invalid_params(msg, None) on failure, which the framework surfaces as JSON-RPC error code -32602 (ErrorCode::INVALID_PARAMS) automatically — no manual mapping needed
  • stdio serve entry: rmcp::ServiceExt::serve(handler, rmcp::transport::stdio()).await?, then service.waiting().await?

Verified by compiling this crate against the real, resolved rmcp = "3.1.4" (workspace pin rmcp = "3.1") and by reading the registry source under rmcp-3.1.4/src/{lib.rs, model.rs, model/content.rs, handler/server/tool.rs, handler/server/router/tool.rs, handler/server/wrapper/parameters.rs} — see shared/ghidra-mcp/Cargo.toml.

Additional API facts nailed down while porting Tasks 15/16 from 0.8.5 to 3.1.4 (kept here so the pinned block stays the single source of truth):

  • #[tool]-annotated methods must return a type implementing IntoCallToolResult. The blanket impls cover T: IntoContents (Content/String/()), CallToolResult itself, rmcp::ErrorData itself, and Result<T, E> where both T and E are IntoCallToolResult (verified in rmcp-3.1.4/src/handler/server/tool.rs) — the same shape as 0.8. A bare -> CallToolResult return does NOT satisfy the trait. Every tool method here therefore returns Result<CallToolResult, rmcp::ErrorData> and always yields Ok(..) — domain failures are encoded as CallToolResult::error(..) inside the Ok, never as the method’s own Err.
  • #[tool_router]/#[tool] attribute values are parsed by darling’s FromMeta, whose default from_expr only accepts Expr::Lit (a literal) or Expr::Group; a bare identifier/path (e.g. a const DESC: &str referenced as description = DESC) is Expr::Path and is rejected at macro expansion. So #[tool(description = "...")] needs the literal text inline; the DESC_* consts in tools.rs stay the single authored copy of the wording and this file’s literals are byte-for-byte copies of them (checked by the tool_descriptions_match_tools_rs_consts regression test below).
  • schemars is a DIRECT dependency of this crate (Cargo.toml’s schemars = { workspace = true }), so the #[derive(schemars::JsonSchema)] on the arg structs below resolves the bare schemars:: path through Rust’s 2021-edition extern-crate prelude — no use rmcp::schemars; needed. (rmcp 3.1 still re-exports schemars at rmcp::schemars when its own schemars feature is on, pulled in transitively by server — rmcp-3.1.4/src/lib.rs — but this file no longer relies on that re-export, matching the direct-dependency style used elsewhere in this workspace, e.g. claude-code/plugins/rtk-mcp-cc/src/bin/mcp.rs.)
  • rmcp::handler::server::router::tool::ToolRouter<Self> is the field type #[tool_router] expects by default (field literally named tool_router) — the module moved one level deeper than 0.8’s rmcp::handler::server::tool::ToolRouter (now under router::tool, not the bare tool module — confirmed via grep -rl "struct ToolRouter" rmcp-3.1.4/src/handler). #[tool_handler]’s generated call_tool/list_tools default to calling Self::tool_router() fresh rather than reading self.tool_router (rmcp-macros-3.1.4/src/tool_handler.rs), so the stored field itself goes unread — #[allow(dead_code)]’d below rather than removed, to keep the conventional shape this workspace’s other MCP servers use.
  • the client-side call-tool params struct (used only by tests/protocol.rs) was renamed from rmcp::model::CallToolRequestParam to CallToolRequestParams and marked #[non_exhaustive] with new MRTR/SEP-2322 fields (input_responses, request_state); build it with CallToolRequestParams::new(name).with_arguments(json_object) instead of a struct literal (rmcp-3.1.4/src/model.rs, the CallToolRequestParams definition).

Structs§

AttachArgs
CommentArgs
DescribeAddressArgs
FindFunctionsArgs
GetDatatypeArgs
GetDisassemblyArgs
GetXrefsArgs
GhidraMcpServer
The MCP server: one attached-worker state shared with the tool methods, plus the rmcp-generated tool router (spec §5).
InspectArgs
ListDataItemsArgs
ListProgramsArgs
ListSegmentsArgs
ListStringsArgs
ListSymbolsArgs
ParamArg
One parameter of a set_prototype request. name is optional — omit (or send “”) to auto-name (param_1…); the worker coerces “” → null before ParameterImpl. type_ serializes as type.
ReadBytesArgs
RenameArgs
ResolveSymbolArgs
SetDatatypeArgs
SetLocalArgs
SetPrototypeArgs

Enums§

CommentChannel
Wire enum for the comment channel. schemars emits a fixed enum in the JSON schema, so a bad value is rejected with -32602 before it reaches the worker. serde lowercase matches the worker’s strings.

Functions§

default_direction 🔒
default_find_limit 🔒
default_limit 🔒
default_list_strings_limit 🔒
default_max_c_lines 🔒
default_read_bytes_length 🔒
err_result 🔒
Turn an ErrorEnvelope into an isError CallToolResult (spec §4.2 domain layer).
graceful_shutdown
Best-effort orderly worker release: a short shutdown RPC, WAIT bounded for exit, then drop (JobObject kills the tree). WorkerProcess.child is a std::process::Child (blocking wait) so this uses a bounded try_wait poll (R6 seat-Q) — a one-shot at exit, not a hot loop.
ok_result 🔒
OK result carrying a JSON value as text.
serve
Serve MCP over stdio until the client disconnects. Kicks off warmup right after the transport is up (spec §5) and performs graceful shutdown on exit (spec §4.6).
shutdown_signal 🔒