1use reqwest::Client;
2
3pub use kontext_dev_core::AccessToken;
4pub use kontext_dev_core::DEFAULT_SCOPE;
5pub use kontext_dev_core::DEFAULT_SERVER_NAME;
6pub use kontext_dev_core::KontextDevConfig;
7
8#[derive(Debug, thiserror::Error)]
9pub enum KontextDevError {
10 #[error(transparent)]
11 Core(#[from] kontext_dev_core::KontextDevCoreError),
12 #[error("Kontext-Dev token request failed for {token_url}")]
13 TokenRequest {
14 token_url: String,
15 source: reqwest::Error,
16 },
17 #[error("Kontext-Dev access token is empty")]
18 EmptyAccessToken,
19}
20
21pub fn build_mcp_url(
22 config: &KontextDevConfig,
23 access_token: &str,
24) -> Result<String, KontextDevError> {
25 kontext_dev_core::build_mcp_url(config, access_token).map_err(KontextDevError::from)
26}
27
28pub async fn request_access_token(
29 config: &KontextDevConfig,
30) -> Result<AccessToken, KontextDevError> {
31 let client = Client::new();
32 let response = client
33 .post(config.token_url.as_str())
34 .form(&[
35 ("grant_type", "client_credentials"),
36 ("client_id", config.client_id.as_str()),
37 ("client_secret", config.client_secret.as_str()),
38 ("scope", config.scope.as_str()),
39 ])
40 .send()
41 .await
42 .map_err(|source| KontextDevError::TokenRequest {
43 token_url: config.token_url.clone(),
44 source,
45 })?
46 .error_for_status()
47 .map_err(|source| KontextDevError::TokenRequest {
48 token_url: config.token_url.clone(),
49 source,
50 })?;
51
52 let token =
53 response
54 .json::<AccessToken>()
55 .await
56 .map_err(|source| KontextDevError::TokenRequest {
57 token_url: config.token_url.clone(),
58 source,
59 })?;
60
61 if token.access_token.is_empty() {
62 return Err(KontextDevError::EmptyAccessToken);
63 }
64
65 Ok(token)
66}