1use serde::Deserialize;
2use serde::Serialize;
3use url::Url;
4
5pub const DEFAULT_SCOPE: &str = "mcp:invoke";
6pub const DEFAULT_SERVER_NAME: &str = "kontext-dev";
7
8fn default_scope() -> String {
9 DEFAULT_SCOPE.to_string()
10}
11
12fn default_server_name() -> String {
13 DEFAULT_SERVER_NAME.to_string()
14}
15
16#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
17pub struct KontextDevConfig {
18 pub mcp_url: String,
19 pub token_url: String,
20 pub client_id: String,
21 pub client_secret: String,
22 #[serde(default = "default_scope")]
23 pub scope: String,
24 #[serde(default = "default_server_name")]
25 pub server_name: String,
26}
27
28#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
29pub struct AccessToken {
30 pub access_token: String,
31 #[serde(default)]
32 pub expires_in: Option<i64>,
33}
34
35#[derive(Debug, thiserror::Error)]
36pub enum KontextDevCoreError {
37 #[error("Kontext-Dev access token is empty")]
38 EmptyAccessToken,
39 #[error("failed to parse {mcp_url}")]
40 InvalidMcpUrl {
41 mcp_url: String,
42 source: url::ParseError,
43 },
44}
45
46pub fn build_mcp_url(
47 config: &KontextDevConfig,
48 access_token: &str,
49) -> Result<String, KontextDevCoreError> {
50 if access_token.is_empty() {
51 return Err(KontextDevCoreError::EmptyAccessToken);
52 }
53
54 let mcp_url = config.mcp_url.as_str();
55 let mut url = Url::parse(mcp_url).map_err(|source| KontextDevCoreError::InvalidMcpUrl {
56 mcp_url: mcp_url.to_string(),
57 source,
58 })?;
59 url.query_pairs_mut()
60 .append_pair("access_key", access_token);
61 Ok(url.to_string())
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use pretty_assertions::assert_eq;
68
69 #[test]
70 fn build_mcp_url_appends_access_key() {
71 let config = KontextDevConfig {
72 mcp_url: "http://localhost:4000/mcp".to_string(),
73 token_url: "http://localhost:4444/oauth2/token".to_string(),
74 client_id: "client".to_string(),
75 client_secret: "secret".to_string(),
76 scope: default_scope(),
77 server_name: default_server_name(),
78 };
79
80 let url = build_mcp_url(&config, "token").expect("build_mcp_url failed");
81 assert_eq!(url, "http://localhost:4000/mcp?access_key=token");
82 }
83}