wowlab_sentinel/mcp/tools/
loadout.rs1use schemars::JsonSchema;
2use serde::Deserialize;
3use sqlx::PgPool;
4use wowlab_loadout::{EnrichedLoadout, apply_decoded_traits, decode_trait_loadout, enrich_loadout};
5use wowlab_types::data::TraitTreeFlat;
6
7#[derive(Debug, Default, Deserialize, JsonSchema)]
9#[schemars(
10 description = "Decode a WoW talent loadout string into selected talents with spell IDs, names, and ranks"
11)]
12pub(crate) struct DecodeLoadoutInput {
13 #[schemars(
14 description = "Base64-encoded talent loadout string (from SimC `talents=` or the game client export)"
15 )]
16 loadout: String,
17}
18
19pub(super) async fn execute(
21 db: &PgPool,
22 input: DecodeLoadoutInput,
23) -> Result<EnrichedLoadout, super::ToolError> {
24 let decoded = decode_trait_loadout(&input.loadout)
25 .map_err(|e| format!("Failed to decode loadout string: {e}"))?;
26
27 let spec_id = i32::from(decoded.spec_id);
28
29 let row: (serde_json::Value,) =
30 sqlx::query_as("SELECT row_to_json(t) FROM game.specs_traits t WHERE spec_id = $1 LIMIT 1")
31 .bind(spec_id)
32 .fetch_one(db)
33 .await
34 .map_err(|e| format!("Failed to fetch trait tree for spec_id {spec_id}: {e}"))?;
35
36 let tree: TraitTreeFlat =
37 serde_json::from_value(row.0).map_err(|e| format!("Failed to parse trait tree: {e}"))?;
38
39 let with_selections = apply_decoded_traits(tree, &decoded);
40
41 Ok(enrich_loadout(&with_selections))
42}
43
44pub(super) async fn handle(
45 db: &PgPool,
46 params: DecodeLoadoutInput,
47) -> Result<rmcp::model::CallToolResult, rmcp::ErrorData> {
48 let result = execute(db, params)
49 .await
50 .map_err(|e| crate::mcp::mcp_error(crate::telemetry::McpTool::DecodeLoadout, &e))?;
51
52 Ok(crate::mcp::json_result(result))
53}