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
implblock (generatesSelf::tool_router()), #[rmcp::tool(description = “…”)] on each async method, #[rmcp::tool_handler] on theimpl ServerHandler for Tblock (wires call_tool/list_tools) — the fully-qualified attribute paths still resolve:rmcp_macrosis re-exported at the crate root viapub use rmcp_macros::*;(rmcp-3.1.4/src/lib.rs) - server info: rmcp::model::ServerConfig (a
#[non_exhaustive]type alias forInitializeResultas 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) toContentBlock(3.1);CallToolResult::success/errortakeVec<ContentBlock>accordingly (rmcp-3.1.4/src/model/content.rs,rmcp-3.1.4/src/model.rsaround theCallToolResultdefinition) - structured content: rmcp::model::CallToolResult::structured(serde_json::Value)
(success, sets
structured_content+ mirrors it as text) andCallToolResult::structured_error(Value)for the error case;CallToolResultalso exposesinto_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 implementingIntoCallToolResult. The blanket impls coverT: IntoContents(Content/String/()),CallToolResultitself,rmcp::ErrorDataitself, andResult<T, E>where bothTandEareIntoCallToolResult(verified inrmcp-3.1.4/src/handler/server/tool.rs) — the same shape as 0.8. A bare-> CallToolResultreturn does NOT satisfy the trait. Every tool method here therefore returnsResult<CallToolResult, rmcp::ErrorData>and always yieldsOk(..)— domain failures are encoded asCallToolResult::error(..)inside the Ok, never as the method’s ownErr.#[tool_router]/#[tool]attribute values are parsed bydarling’sFromMeta, whose defaultfrom_expronly acceptsExpr::Lit(a literal) orExpr::Group; a bare identifier/path (e.g. aconst DESC: &strreferenced asdescription = DESC) isExpr::Pathand is rejected at macro expansion. So#[tool(description = "...")]needs the literal text inline; theDESC_*consts intools.rsstay the single authored copy of the wording and this file’s literals are byte-for-byte copies of them (checked by thetool_descriptions_match_tools_rs_constsregression test below).schemarsis a DIRECT dependency of this crate (Cargo.toml’sschemars = { workspace = true }), so the#[derive(schemars::JsonSchema)]on the arg structs below resolves the bareschemars::path through Rust’s 2021-edition extern-crate prelude — nouse rmcp::schemars;needed. (rmcp 3.1 still re-exportsschemarsatrmcp::schemarswhen its ownschemarsfeature is on, pulled in transitively byserver—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 namedtool_router) — the module moved one level deeper than 0.8’srmcp::handler::server::tool::ToolRouter(now underrouter::tool, not the baretoolmodule — confirmed viagrep -rl "struct ToolRouter" rmcp-3.1.4/src/handler).#[tool_handler]’s generatedcall_tool/list_toolsdefault to callingSelf::tool_router()fresh rather than readingself.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::CallToolRequestParamtoCallToolRequestParamsand marked#[non_exhaustive]with new MRTR/SEP-2322 fields (input_responses,request_state); build it withCallToolRequestParams::new(name).with_arguments(json_object)instead of a struct literal (rmcp-3.1.4/src/model.rs, theCallToolRequestParamsdefinition).
Structs§
- Attach
Args - Comment
Args - Describe
Address Args - Find
Functions Args - GetDatatype
Args - GetDisassembly
Args - GetXrefs
Args - Ghidra
McpServer - The MCP server: one attached-worker state shared with the tool methods, plus the rmcp-generated tool router (spec §5).
- Inspect
Args - List
Data Items Args - List
Programs Args - List
Segments Args - List
Strings Args - List
Symbols Args - Param
Arg - One parameter of a
set_prototyperequest.nameis optional — omit (or send “”) to auto-name (param_1…); the worker coerces “” → null beforeParameterImpl.type_serializes astype. - Read
Bytes Args - Rename
Args - Resolve
Symbol Args - SetDatatype
Args - SetLocal
Args - SetPrototype
Args
Enums§
- Comment
Channel - Wire enum for the
commentchannel. schemars emits a fixedenumin the JSON schema, so a bad value is rejected with -32602 before it reaches the worker. serdelowercasematches 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
ErrorEnvelopeinto an isError CallToolResult (spec §4.2 domain layer). - graceful_
shutdown - Best-effort orderly worker release: a short
shutdownRPC, WAIT bounded for exit, then drop (JobObject kills the tree).WorkerProcess.childis 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 🔒