use std::sync::Arc;
use bevy_brp_mcp_macros::BrpTools;
use bevy_brp_mcp_macros::ToolDescription;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use strum::AsRefStr;
use strum::Display;
use strum::EnumIter;
use strum::EnumString;
use strum::IntoStaticStr;
use super::ToolDef;
use super::annotations::Annotation;
use super::annotations::EnvironmentImpact;
use super::annotations::ToolCategory;
use super::handler::ErasedToolFn;
use super::parameters;
use super::parameters::ParameterBuilder;
use crate::app_tools;
use crate::app_tools::LaunchBevyBinaryParams;
use crate::app_tools::ListBevy;
use crate::app_tools::ListBevyParams;
use crate::app_tools::Shutdown;
use crate::app_tools::ShutdownParams;
use crate::app_tools::Status;
use crate::app_tools::StatusParams;
use crate::brp_tools::AllTypeGuidesParams;
use crate::brp_tools::BevyListWatch;
use crate::brp_tools::BrpAllTypeGuides;
use crate::brp_tools::BrpExecute;
use crate::brp_tools::BrpExtrasScreenshot;
use crate::brp_tools::BrpListActiveWatches;
use crate::brp_tools::BrpListAgentTools;
use crate::brp_tools::BrpStopWatch;
use crate::brp_tools::BrpTypeGuide;
use crate::brp_tools::ClickMouseParams;
use crate::brp_tools::ClickMouseResult;
use crate::brp_tools::DespawnEntityParams;
use crate::brp_tools::DespawnEntityResult;
use crate::brp_tools::DoubleClickMouseParams;
use crate::brp_tools::DoubleClickMouseResult;
use crate::brp_tools::DoubleTapGestureParams;
use crate::brp_tools::DoubleTapGestureResult;
use crate::brp_tools::DragMouseParams;
use crate::brp_tools::DragMouseResult;
use crate::brp_tools::ExecuteParams;
use crate::brp_tools::FindEntitiesByNameParams;
use crate::brp_tools::GetComponentsParams;
use crate::brp_tools::GetComponentsResult;
use crate::brp_tools::GetComponentsWatchParams;
use crate::brp_tools::GetDiagnosticsParams;
use crate::brp_tools::GetDiagnosticsResult;
use crate::brp_tools::GetResourcesParams;
use crate::brp_tools::GetResourcesResult;
use crate::brp_tools::InsertComponentsParams;
use crate::brp_tools::InsertComponentsResult;
use crate::brp_tools::InsertResourcesParams;
use crate::brp_tools::InsertResourcesResult;
use crate::brp_tools::ListAgentToolsParams;
use crate::brp_tools::ListComponentsParams;
use crate::brp_tools::ListComponentsResult;
use crate::brp_tools::ListComponentsWatchParams;
use crate::brp_tools::ListResourcesParams;
use crate::brp_tools::ListResourcesResult;
use crate::brp_tools::MoveMouseParams;
use crate::brp_tools::MoveMouseResult;
use crate::brp_tools::MutateComponentsParams;
use crate::brp_tools::MutateComponentsResult;
use crate::brp_tools::MutateResourcesParams;
use crate::brp_tools::MutateResourcesResult;
use crate::brp_tools::PinchGestureParams;
use crate::brp_tools::PinchGestureResult;
use crate::brp_tools::QueryParams;
use crate::brp_tools::QueryResult;
use crate::brp_tools::RegistrySchemaParams;
use crate::brp_tools::RegistrySchemaResult;
use crate::brp_tools::RemoveComponentsParams;
use crate::brp_tools::RemoveComponentsResult;
use crate::brp_tools::RemoveResourcesParams;
use crate::brp_tools::RemoveResourcesResult;
use crate::brp_tools::ReparentEntitiesParams;
use crate::brp_tools::ReparentEntitiesResult;
use crate::brp_tools::RotationGestureParams;
use crate::brp_tools::RotationGestureResult;
use crate::brp_tools::RpcDiscoverParams;
use crate::brp_tools::RpcDiscoverResult;
use crate::brp_tools::ScreenshotParams;
use crate::brp_tools::ScrollMouseParams;
use crate::brp_tools::ScrollMouseResult;
use crate::brp_tools::SendKeysParams;
use crate::brp_tools::SendKeysResult;
use crate::brp_tools::SendMouseButtonParams;
use crate::brp_tools::SendMouseButtonResult;
use crate::brp_tools::SetWindowTitleParams;
use crate::brp_tools::SetWindowTitleResult;
use crate::brp_tools::SpawnEntityParams;
use crate::brp_tools::SpawnEntityResult;
use crate::brp_tools::StopWatchParams;
use crate::brp_tools::TriggerEventParams;
use crate::brp_tools::TriggerEventResult;
use crate::brp_tools::TypeGuideParams;
use crate::brp_tools::TypeTextParams;
use crate::brp_tools::TypeTextResult;
use crate::brp_tools::WorldFindEntitiesByName;
use crate::brp_tools::WorldGetComponentsWatch;
use crate::log_tools::DeleteLogs;
use crate::log_tools::DeleteLogsParams;
#[cfg(feature = "mcp-debug")]
use crate::log_tools::GetTraceLogPath;
use crate::log_tools::ListLogs;
use crate::log_tools::ListLogsParams;
use crate::log_tools::ReadLog;
use crate::log_tools::ReadLogParams;
#[cfg(feature = "mcp-debug")]
use crate::log_tools::SetTracingLevel;
#[cfg(feature = "mcp-debug")]
use crate::log_tools::SetTracingLevelParams;
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub(super) enum CallInfo {
Local {
mcp_tool: String,
},
Brp {
mcp_tool: String,
brp_method: String,
},
}
#[derive(
AsRefStr,
BrpTools,
Clone,
Copy,
Debug,
Display,
EnumIter,
EnumString,
Eq,
IntoStaticStr,
PartialEq,
ToolDescription,
)]
#[strum(serialize_all = "snake_case")]
#[tool_description(path = "../../help_text")]
pub enum ToolName {
#[brp_tool(
brp_method = "world.list_components",
params = "ListComponentsParams",
result = "ListComponentsResult"
)]
WorldListComponents,
#[brp_tool(
brp_method = "world.get_components",
params = "GetComponentsParams",
result = "GetComponentsResult"
)]
WorldGetComponents,
#[brp_tool(
brp_method = "world.despawn_entity",
params = "DespawnEntityParams",
result = "DespawnEntityResult"
)]
WorldDespawnEntity,
#[brp_tool(
brp_method = "world.insert_components",
params = "InsertComponentsParams",
result = "InsertComponentsResult"
)]
WorldInsertComponents,
#[brp_tool(
brp_method = "world.remove_components",
params = "RemoveComponentsParams",
result = "RemoveComponentsResult"
)]
WorldRemoveComponents,
#[brp_tool(
brp_method = "world.list_resources",
params = "ListResourcesParams",
result = "ListResourcesResult"
)]
WorldListResources,
#[brp_tool(
brp_method = "world.get_resources",
params = "GetResourcesParams",
result = "GetResourcesResult"
)]
WorldGetResources,
#[brp_tool(
brp_method = "world.insert_resources",
params = "InsertResourcesParams",
result = "InsertResourcesResult"
)]
WorldInsertResources,
#[brp_tool(
brp_method = "world.remove_resources",
params = "RemoveResourcesParams",
result = "RemoveResourcesResult"
)]
WorldRemoveResources,
#[brp_tool(
brp_method = "world.mutate_resources",
params = "MutateResourcesParams",
result = "MutateResourcesResult"
)]
WorldMutateResources,
#[brp_tool(
brp_method = "world.mutate_components",
params = "MutateComponentsParams",
result = "MutateComponentsResult"
)]
WorldMutateComponents,
#[brp_tool(
brp_method = "rpc.discover",
params = "RpcDiscoverParams",
result = "RpcDiscoverResult"
)]
RpcDiscover,
#[brp_tool(
brp_method = "world.query",
params = "QueryParams",
result = "QueryResult"
)]
WorldQuery,
WorldFindEntitiesByName,
#[brp_tool(
brp_method = "world.spawn_entity",
params = "SpawnEntityParams",
result = "SpawnEntityResult"
)]
WorldSpawnEntity,
#[brp_tool(
brp_method = "world.trigger_event",
params = "TriggerEventParams",
result = "TriggerEventResult"
)]
WorldTriggerEvent,
#[brp_tool(
brp_method = "registry.schema",
params = "RegistrySchemaParams",
result = "RegistrySchemaResult"
)]
RegistrySchema,
#[brp_tool(
brp_method = "world.reparent_entities",
params = "ReparentEntitiesParams",
result = "ReparentEntitiesResult"
)]
WorldReparentEntities,
#[brp_tool(brp_method = "world.get_components+watch")]
WorldGetComponentsWatch,
#[brp_tool(brp_method = "world.list_components+watch")]
WorldListComponentsWatch,
BrpExecute,
BrpListAgentTools,
#[brp_tool(brp_method = "brp_extras/screenshot")]
BrpExtrasScreenshot,
#[brp_tool(
brp_method = "brp_extras/send_keys",
params = "SendKeysParams",
result = "SendKeysResult"
)]
BrpExtrasSendKeys,
#[brp_tool(
brp_method = "brp_extras/type_text",
params = "TypeTextParams",
result = "TypeTextResult"
)]
BrpExtrasTypeText,
#[brp_tool(
brp_method = "brp_extras/set_window_title",
params = "SetWindowTitleParams",
result = "SetWindowTitleResult"
)]
BrpExtrasSetWindowTitle,
#[brp_tool(
brp_method = "brp_extras/move_mouse",
params = "MoveMouseParams",
result = "MoveMouseResult"
)]
BrpExtrasMoveMouse,
#[brp_tool(
brp_method = "brp_extras/send_mouse_button",
params = "SendMouseButtonParams",
result = "SendMouseButtonResult"
)]
BrpExtrasSendMouseButton,
#[brp_tool(
brp_method = "brp_extras/click_mouse",
params = "ClickMouseParams",
result = "ClickMouseResult"
)]
BrpExtrasClickMouse,
#[brp_tool(
brp_method = "brp_extras/double_click_mouse",
params = "DoubleClickMouseParams",
result = "DoubleClickMouseResult"
)]
BrpExtrasDoubleClickMouse,
#[brp_tool(
brp_method = "brp_extras/drag_mouse",
params = "DragMouseParams",
result = "DragMouseResult"
)]
BrpExtrasDragMouse,
#[brp_tool(
brp_method = "brp_extras/scroll_mouse",
params = "ScrollMouseParams",
result = "ScrollMouseResult"
)]
BrpExtrasScrollMouse,
#[brp_tool(
brp_method = "brp_extras/pinch_gesture",
params = "PinchGestureParams",
result = "PinchGestureResult"
)]
BrpExtrasPinchGesture,
#[brp_tool(
brp_method = "brp_extras/rotation_gesture",
params = "RotationGestureParams",
result = "RotationGestureResult"
)]
BrpExtrasRotationGesture,
#[brp_tool(
brp_method = "brp_extras/double_tap_gesture",
params = "DoubleTapGestureParams",
result = "DoubleTapGestureResult"
)]
BrpExtrasDoubleTapGesture,
#[brp_tool(
brp_method = "brp_extras/get_diagnostics",
params = "GetDiagnosticsParams",
result = "GetDiagnosticsResult"
)]
BrpExtrasGetDiagnostics,
BrpStopWatch,
BrpListActiveWatches,
BrpListBevy,
BrpLaunch,
#[brp_tool(brp_method = "brp_extras/shutdown")]
BrpShutdown,
BrpStatus,
BrpListLogs,
BrpReadLog,
BrpDeleteLogs,
#[cfg(feature = "mcp-debug")]
BrpGetTraceLogPath,
#[cfg(feature = "mcp-debug")]
BrpSetTracingLevel,
BrpTypeGuide,
BrpAllTypeGuides,
}
impl ToolName {
pub(super) fn get_call_info(self) -> CallInfo {
let tool_name = self.to_string();
match self.to_brp_method() {
Some(brp_method) => CallInfo::Brp {
mcp_tool: tool_name,
brp_method: brp_method.as_str().to_string(),
},
None => CallInfo::Local {
mcp_tool: tool_name,
},
}
}
#[allow(
clippy::too_many_lines,
reason = "trivial per-variant constructor calls"
)]
fn get_annotations(self) -> Annotation {
match self {
Self::WorldDespawnEntity => Annotation::new(
"despawn bevy entity",
ToolCategory::Entity,
EnvironmentImpact::DestructiveIdempotent,
),
Self::WorldGetComponents => Annotation::new(
"get component data",
ToolCategory::Component,
EnvironmentImpact::ReadOnly,
),
Self::WorldGetResources => Annotation::new(
"get resource data",
ToolCategory::Resource,
EnvironmentImpact::ReadOnly,
),
Self::WorldInsertComponents => Annotation::new(
"insert components",
ToolCategory::Component,
EnvironmentImpact::AdditiveIdempotent,
),
Self::WorldInsertResources => Annotation::new(
"insert resources",
ToolCategory::Resource,
EnvironmentImpact::AdditiveIdempotent,
),
Self::WorldListComponents => Annotation::new(
"list components",
ToolCategory::Component,
EnvironmentImpact::ReadOnly,
),
Self::WorldListResources => Annotation::new(
"list resources",
ToolCategory::Resource,
EnvironmentImpact::ReadOnly,
),
Self::WorldMutateComponents => Annotation::new(
"mutate components",
ToolCategory::Component,
EnvironmentImpact::AdditiveIdempotent,
),
Self::WorldMutateResources => Annotation::new(
"mutate resources",
ToolCategory::Resource,
EnvironmentImpact::AdditiveIdempotent,
),
Self::WorldQuery => Annotation::new(
"query entities/components",
ToolCategory::Component,
EnvironmentImpact::ReadOnly,
),
Self::WorldFindEntitiesByName => Annotation::new(
"find entities by name",
ToolCategory::Discovery,
EnvironmentImpact::ReadOnly,
),
Self::RegistrySchema => Annotation::new(
"get type schemas using 'registry.schema' method",
ToolCategory::Discovery,
EnvironmentImpact::ReadOnly,
),
Self::WorldRemoveComponents => Annotation::new(
"remove components",
ToolCategory::Component,
EnvironmentImpact::DestructiveIdempotent,
),
Self::WorldRemoveResources => Annotation::new(
"remove resources",
ToolCategory::Resource,
EnvironmentImpact::DestructiveIdempotent,
),
Self::WorldReparentEntities => Annotation::new(
"reparent entities",
ToolCategory::Entity,
EnvironmentImpact::AdditiveIdempotent,
),
Self::RpcDiscover => Annotation::new(
"discover brp methods",
ToolCategory::Discovery,
EnvironmentImpact::ReadOnly,
),
Self::WorldSpawnEntity => Annotation::new(
"spawn entity",
ToolCategory::Entity,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::WorldTriggerEvent => Annotation::new(
"trigger event",
ToolCategory::Event,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExecute => Annotation::new(
"execute brp method",
ToolCategory::DynamicBrp,
EnvironmentImpact::DestructiveNonIdempotent,
),
Self::BrpListAgentTools => Annotation::new(
"list agent tools",
ToolCategory::Discovery,
EnvironmentImpact::ReadOnly,
),
Self::BrpExtrasScreenshot => Annotation::new(
"take screenshot",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasSendKeys => Annotation::new(
"send keys",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasSetWindowTitle => Annotation::new(
"change window title",
ToolCategory::Extras,
EnvironmentImpact::AdditiveIdempotent,
),
Self::BrpExtrasTypeText => Annotation::new(
"type text sequentially",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasMoveMouse => Annotation::new(
"move mouse cursor",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasSendMouseButton => Annotation::new(
"send mouse button",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasClickMouse => Annotation::new(
"click mouse button",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasDoubleClickMouse => Annotation::new(
"double click mouse",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasDragMouse => Annotation::new(
"drag mouse",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasScrollMouse => Annotation::new(
"scroll mouse wheel",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasPinchGesture => Annotation::new(
"pinch gesture",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasRotationGesture => Annotation::new(
"rotation gesture",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasDoubleTapGesture => Annotation::new(
"double tap gesture",
ToolCategory::Extras,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpExtrasGetDiagnostics => Annotation::new(
"get FPS diagnostics",
ToolCategory::Extras,
EnvironmentImpact::ReadOnly,
),
Self::WorldGetComponentsWatch => Annotation::new(
"watch component changes",
ToolCategory::WatchMonitoring,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::WorldListComponentsWatch => Annotation::new(
"watch component list",
ToolCategory::WatchMonitoring,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpDeleteLogs => Annotation::new(
"delete log files",
ToolCategory::Logging,
EnvironmentImpact::DestructiveIdempotent,
),
#[cfg(feature = "mcp-debug")]
Self::BrpGetTraceLogPath => Annotation::new(
"get trace log path",
ToolCategory::Logging,
EnvironmentImpact::ReadOnly,
),
Self::BrpLaunch => Annotation::new(
"launch bevy app or example",
ToolCategory::App,
EnvironmentImpact::AdditiveNonIdempotent,
),
Self::BrpListBevy => Annotation::new(
"list bevy apps and examples",
ToolCategory::App,
EnvironmentImpact::ReadOnly,
),
Self::BrpListActiveWatches => Annotation::new(
"list active watches",
ToolCategory::WatchMonitoring,
EnvironmentImpact::ReadOnly,
),
Self::BrpStopWatch => Annotation::new(
"stop watch",
ToolCategory::WatchMonitoring,
EnvironmentImpact::DestructiveIdempotent,
),
Self::BrpListLogs => Annotation::new(
"list log files",
ToolCategory::Logging,
EnvironmentImpact::ReadOnly,
),
Self::BrpReadLog => Annotation::new(
"read log file",
ToolCategory::Logging,
EnvironmentImpact::ReadOnly,
),
#[cfg(feature = "mcp-debug")]
Self::BrpSetTracingLevel => Annotation::new(
"set tracing level",
ToolCategory::Logging,
EnvironmentImpact::AdditiveIdempotent,
),
Self::BrpStatus => Annotation::new(
"check app status",
ToolCategory::App,
EnvironmentImpact::ReadOnly,
),
Self::BrpShutdown => Annotation::new(
"shutdown bevy app",
ToolCategory::App,
EnvironmentImpact::DestructiveIdempotent,
),
Self::BrpTypeGuide => Annotation::new(
"type guide for components and resources",
ToolCategory::Discovery,
EnvironmentImpact::ReadOnly,
),
Self::BrpAllTypeGuides => Annotation::new(
"get type guides for all registered types",
ToolCategory::Discovery,
EnvironmentImpact::ReadOnly,
),
}
}
#[allow(
clippy::too_many_lines,
reason = "trivial per-variant constructor calls"
)]
fn get_parameters(self) -> Option<fn() -> ParameterBuilder> {
match self {
Self::WorldDespawnEntity => {
Some(parameters::build_parameters_from::<DespawnEntityParams>)
},
Self::WorldGetComponents => {
Some(parameters::build_parameters_from::<GetComponentsParams>)
},
Self::WorldGetResources => {
Some(parameters::build_parameters_from::<GetResourcesParams>)
},
Self::WorldInsertComponents => {
Some(parameters::build_parameters_from::<InsertComponentsParams>)
},
Self::WorldInsertResources => {
Some(parameters::build_parameters_from::<InsertResourcesParams>)
},
Self::WorldListComponents => {
Some(parameters::build_parameters_from::<ListComponentsParams>)
},
Self::WorldListResources => {
Some(parameters::build_parameters_from::<ListResourcesParams>)
},
Self::WorldMutateComponents => {
Some(parameters::build_parameters_from::<MutateComponentsParams>)
},
Self::WorldMutateResources => {
Some(parameters::build_parameters_from::<MutateResourcesParams>)
},
Self::WorldQuery => Some(parameters::build_parameters_from::<QueryParams>),
Self::WorldFindEntitiesByName => {
Some(parameters::build_parameters_from::<FindEntitiesByNameParams>)
},
Self::RegistrySchema => Some(parameters::build_parameters_from::<RegistrySchemaParams>),
Self::WorldRemoveComponents => {
Some(parameters::build_parameters_from::<RemoveComponentsParams>)
},
Self::WorldRemoveResources => {
Some(parameters::build_parameters_from::<RemoveResourcesParams>)
},
Self::WorldReparentEntities => {
Some(parameters::build_parameters_from::<ReparentEntitiesParams>)
},
Self::RpcDiscover => Some(parameters::build_parameters_from::<RpcDiscoverParams>),
Self::WorldSpawnEntity => Some(parameters::build_parameters_from::<SpawnEntityParams>),
Self::WorldTriggerEvent => {
Some(parameters::build_parameters_from::<TriggerEventParams>)
},
Self::BrpExecute => Some(parameters::build_parameters_from::<ExecuteParams>),
Self::BrpListAgentTools => {
Some(parameters::build_parameters_from::<ListAgentToolsParams>)
},
Self::BrpExtrasScreenshot => {
Some(parameters::build_parameters_from::<ScreenshotParams>)
},
Self::BrpExtrasSendKeys => Some(parameters::build_parameters_from::<SendKeysParams>),
Self::BrpExtrasTypeText => Some(parameters::build_parameters_from::<TypeTextParams>),
Self::BrpExtrasSetWindowTitle => {
Some(parameters::build_parameters_from::<SetWindowTitleParams>)
},
Self::BrpExtrasMoveMouse => Some(parameters::build_parameters_from::<MoveMouseParams>),
Self::BrpExtrasSendMouseButton => {
Some(parameters::build_parameters_from::<SendMouseButtonParams>)
},
Self::BrpExtrasClickMouse => {
Some(parameters::build_parameters_from::<ClickMouseParams>)
},
Self::BrpExtrasDoubleClickMouse => {
Some(parameters::build_parameters_from::<DoubleClickMouseParams>)
},
Self::BrpExtrasDragMouse => Some(parameters::build_parameters_from::<DragMouseParams>),
Self::BrpExtrasScrollMouse => {
Some(parameters::build_parameters_from::<ScrollMouseParams>)
},
Self::BrpExtrasPinchGesture => {
Some(parameters::build_parameters_from::<PinchGestureParams>)
},
Self::BrpExtrasRotationGesture => {
Some(parameters::build_parameters_from::<RotationGestureParams>)
},
Self::BrpExtrasDoubleTapGesture => {
Some(parameters::build_parameters_from::<DoubleTapGestureParams>)
},
Self::BrpExtrasGetDiagnostics => {
Some(parameters::build_parameters_from::<GetDiagnosticsParams>)
},
Self::WorldGetComponentsWatch => {
Some(parameters::build_parameters_from::<GetComponentsWatchParams>)
},
Self::WorldListComponentsWatch => {
Some(parameters::build_parameters_from::<ListComponentsWatchParams>)
},
Self::BrpDeleteLogs => Some(parameters::build_parameters_from::<DeleteLogsParams>),
#[cfg(feature = "mcp-debug")]
Self::BrpGetTraceLogPath => None,
Self::BrpListActiveWatches => None,
Self::BrpListBevy => Some(parameters::build_parameters_from::<ListBevyParams>),
Self::BrpLaunch => Some(parameters::build_parameters_from::<LaunchBevyBinaryParams>),
Self::BrpStopWatch => Some(parameters::build_parameters_from::<StopWatchParams>),
Self::BrpListLogs => Some(parameters::build_parameters_from::<ListLogsParams>),
Self::BrpReadLog => Some(parameters::build_parameters_from::<ReadLogParams>),
#[cfg(feature = "mcp-debug")]
Self::BrpSetTracingLevel => {
Some(parameters::build_parameters_from::<SetTracingLevelParams>)
},
Self::BrpStatus => Some(parameters::build_parameters_from::<StatusParams>),
Self::BrpShutdown => Some(parameters::build_parameters_from::<ShutdownParams>),
Self::BrpTypeGuide => Some(parameters::build_parameters_from::<TypeGuideParams>),
Self::BrpAllTypeGuides => {
Some(parameters::build_parameters_from::<AllTypeGuidesParams>)
},
}
}
fn create_handler(self) -> Arc<dyn ErasedToolFn> {
match self {
Self::WorldDespawnEntity => Arc::new(WorldDespawnEntity),
Self::WorldGetComponents => Arc::new(WorldGetComponents),
Self::WorldGetResources => Arc::new(WorldGetResources),
Self::WorldInsertComponents => Arc::new(WorldInsertComponents),
Self::WorldInsertResources => Arc::new(WorldInsertResources),
Self::WorldListComponents => Arc::new(WorldListComponents),
Self::WorldListResources => Arc::new(WorldListResources),
Self::WorldMutateComponents => Arc::new(WorldMutateComponents),
Self::WorldMutateResources => Arc::new(WorldMutateResources),
Self::WorldQuery => Arc::new(WorldQuery),
Self::WorldFindEntitiesByName => Arc::new(WorldFindEntitiesByName),
Self::RegistrySchema => Arc::new(RegistrySchema),
Self::WorldRemoveComponents => Arc::new(WorldRemoveComponents),
Self::WorldRemoveResources => Arc::new(WorldRemoveResources),
Self::WorldReparentEntities => Arc::new(WorldReparentEntities),
Self::RpcDiscover => Arc::new(RpcDiscover),
Self::WorldSpawnEntity => Arc::new(WorldSpawnEntity),
Self::WorldTriggerEvent => Arc::new(WorldTriggerEvent),
Self::BrpExtrasScreenshot => Arc::new(BrpExtrasScreenshot),
Self::BrpExtrasSendKeys => Arc::new(BrpExtrasSendKeys),
Self::BrpExtrasTypeText => Arc::new(BrpExtrasTypeText),
Self::BrpExtrasSetWindowTitle => Arc::new(BrpExtrasSetWindowTitle),
Self::BrpExtrasMoveMouse => Arc::new(BrpExtrasMoveMouse),
Self::BrpExtrasSendMouseButton => Arc::new(BrpExtrasSendMouseButton),
Self::BrpExtrasClickMouse => Arc::new(BrpExtrasClickMouse),
Self::BrpExtrasDoubleClickMouse => Arc::new(BrpExtrasDoubleClickMouse),
Self::BrpExtrasDragMouse => Arc::new(BrpExtrasDragMouse),
Self::BrpExtrasScrollMouse => Arc::new(BrpExtrasScrollMouse),
Self::BrpExtrasPinchGesture => Arc::new(BrpExtrasPinchGesture),
Self::BrpExtrasRotationGesture => Arc::new(BrpExtrasRotationGesture),
Self::BrpExtrasDoubleTapGesture => Arc::new(BrpExtrasDoubleTapGesture),
Self::BrpExtrasGetDiagnostics => Arc::new(BrpExtrasGetDiagnostics),
Self::BrpExecute => Arc::new(BrpExecute),
Self::BrpListAgentTools => Arc::new(BrpListAgentTools),
Self::WorldGetComponentsWatch => Arc::new(WorldGetComponentsWatch),
Self::WorldListComponentsWatch => Arc::new(BevyListWatch),
Self::BrpListActiveWatches => Arc::new(BrpListActiveWatches),
Self::BrpStopWatch => Arc::new(BrpStopWatch),
Self::BrpTypeGuide => Arc::new(BrpTypeGuide),
Self::BrpAllTypeGuides => Arc::new(BrpAllTypeGuides),
Self::BrpDeleteLogs => Arc::new(DeleteLogs),
#[cfg(feature = "mcp-debug")]
Self::BrpGetTraceLogPath => Arc::new(GetTraceLogPath),
Self::BrpLaunch => Arc::new(app_tools::create_launch_handler()),
Self::BrpListBevy => Arc::new(ListBevy),
Self::BrpListLogs => Arc::new(ListLogs),
Self::BrpReadLog => Arc::new(ReadLog),
#[cfg(feature = "mcp-debug")]
Self::BrpSetTracingLevel => Arc::new(SetTracingLevel),
Self::BrpStatus => Arc::new(Status),
Self::BrpShutdown => Arc::new(Shutdown),
}
}
pub(super) fn to_tool_def(self) -> ToolDef {
ToolDef {
tool_name: self,
annotations: self.get_annotations(),
handler: self.create_handler(),
parameters: self.get_parameters(),
}
}
pub(super) fn short_title(self) -> String { self.get_annotations().title }
}
#[cfg(test)]
mod tests {
use rmcp::model::ToolAnnotations;
use serde_json::Value;
use super::ToolName;
const CATALOG_ENTRY_NAME: &str = "test_multiply";
#[test]
fn brp_execute_annotations_are_conservative() {
let annotations = ToolAnnotations::from(ToolName::BrpExecute.get_annotations());
assert_eq!(annotations.read_only_hint, Some(false));
assert_eq!(annotations.destructive_hint, Some(true));
assert_eq!(annotations.idempotent_hint, Some(false));
}
#[test]
fn agent_catalog_is_a_registered_read_only_discovery_tool() {
let tool_name = ToolName::BrpListAgentTools;
let annotations = ToolAnnotations::from(tool_name.get_annotations());
let definitions = crate::tool::get_all_tool_definitions();
assert_eq!(tool_name.to_string(), "brp_list_agent_tools");
assert_eq!(tool_name.to_brp_method(), None);
assert_eq!(annotations.read_only_hint, Some(true));
assert_eq!(annotations.destructive_hint, None);
assert!(
definitions
.iter()
.any(|definition| definition.tool_name == tool_name)
);
assert!(
definitions
.iter()
.all(|definition| definition.name() != CATALOG_ENTRY_NAME)
);
}
#[test]
fn agent_catalog_schema_registers_only_the_port() {
let parameters = ToolName::BrpListAgentTools.get_parameters();
assert!(parameters.is_some());
if let Some(build_parameters) = parameters {
let schema = build_parameters().build();
let properties = schema.get("properties").and_then(Value::as_object);
assert!(properties.is_some());
if let Some(properties) = properties {
assert_eq!(properties.len(), 1);
assert!(properties.contains_key("port"));
}
assert!(schema.get("required").is_none());
}
}
#[test]
fn agent_catalog_help_cross_links_discovery_and_execution_without_native_tools() {
let catalog_help = ToolName::BrpListAgentTools
.description()
.split_whitespace()
.collect::<Vec<_>>()
.join(" ");
let execute_help = ToolName::BrpExecute.description();
let discover_help = ToolName::RpcDiscover.description();
assert!(catalog_help.contains("rpc_discover"));
assert!(catalog_help.contains("brp_execute"));
assert!(catalog_help.contains("not native MCP tools"));
assert!(execute_help.contains("brp_list_agent_tools"));
assert!(execute_help.contains("rpc.discover"));
assert!(discover_help.contains("brp_list_agent_tools"));
assert!(discover_help.contains("brp_execute"));
assert!(!execute_help.contains("native per-entry"));
assert!(!discover_help.contains("native per-entry"));
}
#[test]
fn mcp_service_has_no_agent_catalog_state() {
let service_source = include_str!("../mcp_service.rs");
assert!(!service_source.contains("RemoteCatalog"));
assert!(!service_source.contains("agent_catalog"));
assert!(!service_source.contains("tools/list_changed"));
}
#[test]
fn name_discovery_is_a_registered_read_only_local_tool() {
let tool_name = ToolName::WorldFindEntitiesByName;
let annotations = ToolAnnotations::from(tool_name.get_annotations());
assert_eq!(tool_name.to_string(), "world_find_entities_by_name");
assert_eq!(tool_name.to_brp_method(), None);
assert_eq!(annotations.read_only_hint, Some(true));
assert_eq!(annotations.destructive_hint, None);
assert!(tool_name.description().contains("standard BRP"));
assert!(
crate::tool::get_all_tool_definitions()
.iter()
.any(|definition| definition.tool_name == tool_name)
);
}
#[test]
fn name_discovery_schema_registers_typed_parameters() {
let parameters = ToolName::WorldFindEntitiesByName.get_parameters();
assert!(parameters.is_some());
if let Some(build_parameters) = parameters {
let schema = build_parameters().build();
let properties = schema.get("properties").and_then(Value::as_object);
assert!(properties.is_some());
if let Some(properties) = properties {
assert!(properties.contains_key("name"));
assert!(properties.contains_key("match_mode"));
assert!(properties.contains_key("port"));
}
assert_eq!(schema.get("required"), Some(&serde_json::json!(["name"])));
}
}
}