Skip to main content

embacle_mcp/tools/
provider.rs

1// ABOUTME: MCP tools for getting and setting the active LLM provider
2// ABOUTME: Maps provider names to embacle CliRunnerType for runtime provider switching
3//
4// SPDX-License-Identifier: Apache-2.0
5// Copyright (c) 2026 dravr.ai
6
7use async_trait::async_trait;
8use serde_json::{json, Value};
9
10use dravr_tronc::mcp::schema::{Tool, ToolResponse};
11use dravr_tronc::{McpTool, ToolContext};
12
13use crate::runner::{parse_runner_type, valid_provider_names, ALL_PROVIDERS};
14use crate::state::{ServerState, SharedState};
15
16/// Returns the currently active LLM provider and available providers
17pub struct GetProvider;
18
19#[async_trait]
20impl McpTool<ServerState> for GetProvider {
21    fn definition(&self) -> Tool {
22        Tool {
23            name: "get_provider".to_owned(),
24            description: "Get the active LLM provider and list all available providers".to_owned(),
25            input_schema: json!({
26                "type": "object",
27                "properties": {}
28            }),
29            annotations: None,
30        }
31    }
32
33    async fn execute(
34        &self,
35        state: &SharedState,
36        _ctx: &ToolContext,
37        _arguments: Value,
38    ) -> ToolResponse {
39        let active = state.active_provider().await;
40        let all: Vec<String> = ALL_PROVIDERS.iter().map(ToString::to_string).collect();
41
42        ToolResponse::text(
43            json!({
44                "active_provider": active.to_string(),
45                "available_providers": all
46            })
47            .to_string(),
48        )
49    }
50}
51
52/// Switches the active LLM provider (resets the model selection)
53pub struct SetProvider;
54
55#[async_trait]
56impl McpTool<ServerState> for SetProvider {
57    fn definition(&self) -> Tool {
58        let provider_names: Vec<String> = ALL_PROVIDERS.iter().map(ToString::to_string).collect();
59
60        Tool {
61            name: "set_provider".to_owned(),
62            description: "Set the active LLM provider for prompt dispatch".to_owned(),
63            input_schema: json!({
64                "type": "object",
65                "properties": {
66                    "provider": {
67                        "type": "string",
68                        "description": "Provider name",
69                        "enum": provider_names
70                    }
71                },
72                "required": ["provider"]
73            }),
74            annotations: None,
75        }
76    }
77
78    async fn execute(
79        &self,
80        state: &SharedState,
81        _ctx: &ToolContext,
82        arguments: Value,
83    ) -> ToolResponse {
84        let Some(provider_str) = arguments.get("provider").and_then(Value::as_str) else {
85            return ToolResponse::error("Missing 'provider' argument".to_owned());
86        };
87
88        let Some(provider) = parse_runner_type(provider_str) else {
89            return ToolResponse::error(format!(
90                "Unknown provider: {provider_str}. Valid: {}",
91                valid_provider_names()
92            ));
93        };
94
95        state.set_active_provider(provider).await;
96
97        ToolResponse::text(
98            json!({
99                "active_provider": provider.to_string(),
100                "status": "active"
101            })
102            .to_string(),
103        )
104    }
105}