Skip to main content

kontext_dev_sdk/
server.rs

1use std::future::Future;
2use std::pin::Pin;
3
4use crate::KontextDevClient;
5use crate::KontextDevConfig;
6use crate::KontextDevError;
7
8pub type IntegrationName = String;
9
10#[derive(Clone, Debug)]
11pub enum KnownIntegration {
12    Github,
13    Slack,
14    Notion,
15    Linear,
16    Google,
17    Custom(String),
18}
19
20impl From<KnownIntegration> for String {
21    fn from(value: KnownIntegration) -> Self {
22        match value {
23            KnownIntegration::Github => "github".to_string(),
24            KnownIntegration::Slack => "slack".to_string(),
25            KnownIntegration::Notion => "notion".to_string(),
26            KnownIntegration::Linear => "linear".to_string(),
27            KnownIntegration::Google => "google".to_string(),
28            KnownIntegration::Custom(value) => value,
29        }
30    }
31}
32
33#[derive(Clone, Debug)]
34pub struct IntegrationCredential {
35    pub integration_id: String,
36    pub token: String,
37    pub authorization: String,
38}
39
40#[derive(Clone, Debug)]
41pub struct IntegrationResolvedCredentials {
42    pub integration_id: String,
43    pub authorization: String,
44}
45
46#[derive(Clone, Debug, Default)]
47pub struct MiddlewareOptions {
48    pub resource_server_url: Option<String>,
49}
50
51#[derive(Clone, Debug)]
52pub struct KontextOptions {
53    pub config: KontextDevConfig,
54}
55
56pub type McpServerFactory<T> = fn() -> T;
57pub type McpServerOrFactory<T> = Result<T, McpServerFactory<T>>;
58
59pub trait OAuthTokenVerifier: Send + Sync {
60    fn verify<'a>(
61        &'a self,
62        token: &'a str,
63    ) -> Pin<Box<dyn Future<Output = Result<(), KontextDevError>> + Send + 'a>>;
64}
65
66#[derive(Clone)]
67pub struct Kontext {
68    client: KontextDevClient,
69}
70
71impl Kontext {
72    pub fn new(options: KontextOptions) -> Self {
73        Self {
74            client: KontextDevClient::new(options.config),
75        }
76    }
77
78    pub fn client(&self) -> &KontextDevClient {
79        &self.client
80    }
81
82    pub async fn require(
83        &self,
84        integration: impl Into<IntegrationName>,
85        gateway_access_token: &str,
86    ) -> Result<IntegrationResolvedCredentials, KontextDevError> {
87        let integration_id = integration.into();
88        let status = self
89            .client
90            .integration_connection_status(gateway_access_token, &integration_id)
91            .await?;
92
93        if !status.connected {
94            return Err(KontextDevError::IntegrationOAuthInit {
95                message: format!("integration `{integration_id}` is not connected for this user"),
96            });
97        }
98
99        Ok(IntegrationResolvedCredentials {
100            integration_id,
101            authorization: format!("Bearer {gateway_access_token}"),
102        })
103    }
104
105    pub async fn require_credentials(
106        &self,
107        integration: impl Into<IntegrationName>,
108        gateway_access_token: &str,
109    ) -> Result<IntegrationCredential, KontextDevError> {
110        let integration_id = integration.into();
111        let resolved = self
112            .require(integration_id.clone(), gateway_access_token)
113            .await?;
114
115        Ok(IntegrationCredential {
116            integration_id,
117            token: gateway_access_token.to_string(),
118            authorization: resolved.authorization,
119        })
120    }
121}