use std::sync::Arc;
use serde_json::{Map, Value};
use crate::application::server::context::PeerSession;
use crate::application::server::{ListKind, ListSubscription, Server};
use crate::application::session::DynFrameSink;
use crate::domain::Feature;
pub const SUBSCRIPTION_ID_KEY: &str = "io.modelcontextprotocol/subscriptionId";
const FILTER: [(&str, Feature, ListKind); 3] = [
(
"toolsListChanged",
Feature::ToolsListChanged,
ListKind::Tools,
),
(
"promptsListChanged",
Feature::PromptsListChanged,
ListKind::Prompts,
),
(
"resourcesListChanged",
Feature::ResourcesListChanged,
ListKind::Resources,
),
];
#[derive(Debug, Clone, Default)]
pub struct Honoured {
pub filter: Map<String, Value>,
pub kinds: Vec<ListKind>,
pub resources: Vec<String>,
}
#[must_use]
pub fn honour(requested: &Map<String, Value>, peer: &PeerSession, server: &Server) -> Honoured {
let mut honoured = Honoured::default();
for (member, feature, kind) in FILTER {
if requested.get(member).and_then(Value::as_bool) == Some(true) && peer.allows(feature) {
honoured.filter.insert(member.to_owned(), Value::Bool(true));
honoured.kinds.push(kind);
}
}
if peer.allows(Feature::ResourcesSubscribe) {
honoured.resources = requested
.get("resourceSubscriptions")
.and_then(Value::as_array)
.into_iter()
.flatten()
.filter_map(Value::as_str)
.filter(|uri| server.reader(uri).is_some())
.map(ToOwned::to_owned)
.collect();
}
if !honoured.resources.is_empty() {
honoured.filter.insert(
"resourceSubscriptions".to_owned(),
Value::Array(
honoured
.resources
.iter()
.cloned()
.map(Value::String)
.collect(),
),
);
}
honoured
}
#[must_use]
pub fn tag(id: &Value) -> Value {
serde_json::json!({ SUBSCRIPTION_ID_KEY: id })
}
#[must_use]
pub fn acknowledgement(honoured: &Honoured, meta: &Value) -> Value {
serde_json::json!({
"jsonrpc": "2.0",
"method": "notifications/subscriptions/acknowledged",
"params": {
"notifications": honoured.filter,
"_meta": meta,
},
})
}
#[must_use]
pub fn start(
server: &Server,
sink: &Arc<dyn DynFrameSink>,
peer: &PeerSession,
honoured: &Honoured,
meta: &Value,
) -> ListSubscription {
let handle = server.handle();
let subscription = handle.subscribe(sink, peer.profile(), &honoured.kinds, Some(meta));
for uri in &honoured.resources {
handle.watch_resource(sink, uri, Some(meta));
}
subscription
}