mcp: declare tools capability in ServerInfo
rmcp 0.1.5's #[tool(tool_box)] macro doesn't backfill
ServerInfo::capabilities. Without an explicit ToolsCapability,
clients read "capabilities":{} from initialize and skip tools/list
entirely — the server looks connected (instructions field lands)
but the tool surface is empty. Claude Code's MCP log:
"hasTools":false,"hasPrompts":false,"hasResources":false
Fix: capabilities = ServerCapabilities::builder().enable_tools().build()
in get_info(). Adds a regression test on the wire shape.
This commit is contained in:
parent
1e7e2bcab6
commit
0a658cc4de
1 changed files with 32 additions and 1 deletions
|
|
@ -36,7 +36,10 @@ use aldabra_core::{
|
||||||
InputUtxo, Network, PaymentKey, PlutusExUnits, PlutusInput, PlutusVersion, PolicySpec,
|
InputUtxo, Network, PaymentKey, PlutusExUnits, PlutusInput, PlutusVersion, PolicySpec,
|
||||||
ProtocolParams, StakeKey, DEFAULT_EX_UNITS,
|
ProtocolParams, StakeKey, DEFAULT_EX_UNITS,
|
||||||
};
|
};
|
||||||
use rmcp::{model::ServerInfo, schemars, tool, ServerHandler};
|
use rmcp::{
|
||||||
|
model::{ServerCapabilities, ServerInfo},
|
||||||
|
schemars, tool, ServerHandler,
|
||||||
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
/// MCP-facing asset spec — separate from `aldabra_core::AssetSpec`
|
/// MCP-facing asset spec — separate from `aldabra_core::AssetSpec`
|
||||||
|
|
@ -1268,7 +1271,13 @@ impl WalletService {
|
||||||
#[tool(tool_box)]
|
#[tool(tool_box)]
|
||||||
impl ServerHandler for WalletService {
|
impl ServerHandler for WalletService {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
|
// Declare `tools` capability explicitly. rmcp 0.1.5's `#[tool(tool_box)]` macro
|
||||||
|
// does NOT backfill ServerInfo::capabilities; without this line Claude Code (and
|
||||||
|
// any spec-compliant MCP client) reads `"capabilities": {}` from the initialize
|
||||||
|
// response and skips `tools/list` entirely. The instructions field still lands,
|
||||||
|
// so the failure mode is silent — server appears connected, no tool surface.
|
||||||
ServerInfo {
|
ServerInfo {
|
||||||
|
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||||
instructions: Some(
|
instructions: Some(
|
||||||
"aldabra — Cardano lite wallet over MCP. wallet_*: read (address/balance/utxos/network/stake_address), send (wallet_send with optional inline datum for script locks, wallet_send_unsigned + wallet_sign_partial + wallet_submit_signed_tx for cold/multi-sig, wallet_tx_status), mint (wallet_policy_create, wallet_mint with CIP-25 metadata, wallet_mint_cip68_nft for ref+user NFT pairs, wallet_mint_unsigned), Plutus (wallet_script_spend), stake (wallet_stake_delegate). chain_*: read-only Koios passthroughs (chain_tx_info, chain_address_info, chain_pool_list, chain_pool_info, chain_epoch_params, chain_asset_info, chain_account_info, chain_tip) — for inspecting the chain at addresses/txs/pools beyond this wallet.".into(),
|
"aldabra — Cardano lite wallet over MCP. wallet_*: read (address/balance/utxos/network/stake_address), send (wallet_send with optional inline datum for script locks, wallet_send_unsigned + wallet_sign_partial + wallet_submit_signed_tx for cold/multi-sig, wallet_tx_status), mint (wallet_policy_create, wallet_mint with CIP-25 metadata, wallet_mint_cip68_nft for ref+user NFT pairs, wallet_mint_unsigned), Plutus (wallet_script_spend), stake (wallet_stake_delegate). chain_*: read-only Koios passthroughs (chain_tx_info, chain_address_info, chain_pool_list, chain_pool_info, chain_epoch_params, chain_asset_info, chain_account_info, chain_tip) — for inspecting the chain at addresses/txs/pools beyond this wallet.".into(),
|
||||||
),
|
),
|
||||||
|
|
@ -1276,3 +1285,25 @@ impl ServerHandler for WalletService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod server_info_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn capabilities_builder_declares_tools() {
|
||||||
|
// Regression for the silent "hasTools:false" bug. Claude Code reads
|
||||||
|
// `serverCapabilities.tools` from initialize and skips `tools/list`
|
||||||
|
// entirely if it's missing — leaving the server connected but with
|
||||||
|
// zero callable tools. If anyone refactors `get_info()` and drops
|
||||||
|
// the `enable_tools()` call, this test catches it.
|
||||||
|
let cap = ServerCapabilities::builder().enable_tools().build();
|
||||||
|
assert!(cap.tools.is_some(), "tools capability must be declared");
|
||||||
|
|
||||||
|
let wire = serde_json::to_value(&cap).expect("ServerCapabilities serializes");
|
||||||
|
assert!(
|
||||||
|
wire.get("tools").is_some(),
|
||||||
|
"wire-format capabilities must include 'tools' key, got: {wire}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue