feat: upgrade to rmcp 0.12.0 and sacp 10.0.0 by removing SSE transport (#6304)

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Adrian Cole
2026-01-02 07:07:53 +08:00
committed by GitHub
parent 38fcf4f374
commit 5a5312a2e6
51 changed files with 508 additions and 931 deletions
@@ -302,6 +302,7 @@ impl McpClientTrait for ChatRecallClient {
Ok(ListToolsResult {
tools: Self::get_tools(),
next_cursor: None,
meta: None,
})
}
@@ -835,6 +835,7 @@ impl McpClientTrait for CodeExecutionClient {
}),
],
next_cursor: None,
meta: None,
})
}
+9 -31
View File
@@ -233,27 +233,18 @@ impl Envs {
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema, PartialEq)]
#[serde(tag = "type")]
pub enum ExtensionConfig {
/// Server-sent events client with a URI endpoint
/// SSE transport is no longer supported - kept only for config file compatibility
#[serde(rename = "sse")]
Sse {
/// The name used to identify this extension
#[serde(default)]
#[schema(required)]
name: String,
#[serde(default)]
#[serde(deserialize_with = "deserialize_null_with_default")]
#[schema(required)]
description: String,
uri: String,
#[serde(default)]
envs: Envs,
#[serde(default)]
env_keys: Vec<String>,
// NOTE: set timeout to be optional for compatibility.
// However, new configurations should include this field.
timeout: Option<u64>,
#[serde(default)]
bundled: Option<bool>,
#[serde(default)]
available_tools: Vec<String>,
uri: Option<String>,
},
/// Standard I/O client with command and arguments
#[serde(rename = "stdio")]
@@ -379,19 +370,6 @@ impl Default for ExtensionConfig {
}
impl ExtensionConfig {
pub fn sse<S: Into<String>, T: Into<u64>>(name: S, uri: S, description: S, timeout: T) -> Self {
Self::Sse {
name: name.into(),
uri: uri.into(),
envs: Envs::default(),
env_keys: Vec::new(),
description: description.into(),
timeout: Some(timeout.into()),
bundled: None,
available_tools: Vec::new(),
}
}
pub fn streamable_http<S: Into<String>, T: Into<u64>>(
name: S,
uri: S,
@@ -499,10 +477,8 @@ impl ExtensionConfig {
/// Check if a tool should be available to the LLM
pub fn is_tool_available(&self, tool_name: &str) -> bool {
let available_tools = match self {
Self::Sse {
available_tools, ..
}
| Self::StreamableHttp {
Self::Sse { .. } => return false, // SSE is unsupported
Self::StreamableHttp {
available_tools, ..
}
| Self::Stdio {
@@ -531,7 +507,9 @@ impl ExtensionConfig {
impl std::fmt::Display for ExtensionConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ExtensionConfig::Sse { name, uri, .. } => write!(f, "SSE({}: {})", name, uri),
ExtensionConfig::Sse { name, .. } => {
write!(f, "SSE({}: unsupported)", name)
}
ExtensionConfig::StreamableHttp { name, uri, .. } => {
write!(f, "StreamableHttp({}: {})", name, uri)
}
+7 -22
View File
@@ -9,8 +9,7 @@ use rmcp::transport::streamable_http_client::{
AuthRequiredError, StreamableHttpClientTransportConfig, StreamableHttpError,
};
use rmcp::transport::{
ConfigureCommandExt, DynamicTransportError, SseClientTransport, StreamableHttpClientTransport,
TokioChildProcess,
ConfigureCommandExt, DynamicTransportError, StreamableHttpClientTransport, TokioChildProcess,
};
use std::collections::HashMap;
use std::option::Option;
@@ -481,25 +480,10 @@ impl ExtensionManager {
let mut temp_dir = None;
let client: Box<dyn McpClientTrait> = match &config {
ExtensionConfig::Sse { uri, timeout, .. } => {
let transport = SseClientTransport::start(uri.to_string()).await.map_err(
|transport_error| {
ClientInitializeError::transport::<SseClientTransport<reqwest::Client>>(
transport_error,
"connect",
)
},
)?;
Box::new(
McpClient::connect(
transport,
Duration::from_secs(
timeout.unwrap_or(crate::config::DEFAULT_EXTENSION_TIMEOUT),
),
self.provider.clone(),
)
.await?,
)
ExtensionConfig::Sse { .. } => {
return Err(ExtensionError::ConfigError(
"SSE is unsupported, migrate to streamable_http".to_string(),
));
}
ExtensionConfig::StreamableHttp {
uri,
@@ -1243,8 +1227,8 @@ impl ExtensionManager {
description
}
}
ExtensionConfig::Sse { .. } => "SSE extension (unsupported)",
ExtensionConfig::Platform { description, .. }
| ExtensionConfig::Sse { description, .. }
| ExtensionConfig::StreamableHttp { description, .. }
| ExtensionConfig::Stdio { description, .. }
| ExtensionConfig::Frontend { description, .. }
@@ -1419,6 +1403,7 @@ mod tests {
),
],
next_cursor: None,
meta: None,
})
}
@@ -410,6 +410,7 @@ impl McpClientTrait for ExtensionManagerClient {
Ok(ListToolsResult {
tools: self.get_tools().await,
next_cursor: None,
meta: None,
})
}
@@ -292,6 +292,7 @@ impl McpClientTrait for SkillsClient {
Ok(ListToolsResult {
tools,
next_cursor: None,
meta: None,
})
}
@@ -193,6 +193,7 @@ impl McpClientTrait for TodoClient {
Ok(ListToolsResult {
tools: Self::get_tools(),
next_cursor: None,
meta: None,
})
}
+23 -2
View File
@@ -41,8 +41,8 @@ fn get_extensions_map() -> IndexMap<String, ExtensionEntry> {
let mut extensions_map = IndexMap::with_capacity(raw.len());
for (k, v) in raw {
match (k, serde_yaml::from_value::<ExtensionEntry>(v)) {
(serde_yaml::Value::String(s), Ok(entry)) => {
extensions_map.insert(s, entry);
(serde_yaml::Value::String(key), Ok(entry)) => {
extensions_map.insert(key, entry);
}
(k, v) => {
warn!(
@@ -134,3 +134,24 @@ pub fn get_enabled_extensions() -> Vec<ExtensionConfig> {
.map(|ext| ext.config)
.collect()
}
pub fn get_warnings() -> Vec<String> {
let raw: Mapping = Config::global()
.get_param(EXTENSIONS_CONFIG_KEY)
.unwrap_or_default();
let mut warnings = Vec::new();
for (k, v) in raw {
if let (serde_yaml::Value::String(key), Ok(entry)) =
(k, serde_yaml::from_value::<ExtensionEntry>(v))
{
if matches!(entry.config, ExtensionConfig::Sse { .. }) {
warnings.push(format!(
"'{}': SSE is unsupported, migrate to streamable_http",
key
));
}
}
}
warnings
}
+2 -1
View File
@@ -15,7 +15,8 @@ pub use declarative_providers::DeclarativeProviderConfig;
pub use experiments::ExperimentManager;
pub use extensions::{
get_all_extension_names, get_all_extensions, get_enabled_extensions, get_extension_by_name,
is_extension_enabled, remove_extension, set_extension, set_extension_enabled, ExtensionEntry,
get_warnings, is_extension_enabled, remove_extension, set_extension, set_extension_enabled,
ExtensionEntry,
};
pub use goose_mode::GooseMode;
pub use permission::PermissionManager;
@@ -7,22 +7,6 @@ use std::collections::HashMap;
#[derive(Deserialize)]
#[serde(tag = "type")]
enum RecipeExtensionConfigInternal {
#[serde(rename = "sse")]
Sse {
name: String,
#[serde(default)]
description: Option<String>,
uri: String,
#[serde(default)]
envs: Envs,
#[serde(default)]
env_keys: Vec<String>,
timeout: Option<u64>,
#[serde(default)]
bundled: Option<bool>,
#[serde(default)]
available_tools: Vec<String>,
},
#[serde(rename = "stdio")]
Stdio {
name: String,
@@ -127,15 +111,7 @@ macro_rules! map_recipe_extensions {
impl From<RecipeExtensionConfigInternal> for ExtensionConfig {
fn from(internal_variant: RecipeExtensionConfigInternal) -> Self {
map_recipe_extensions!(
internal_variant;
Sse {
uri,
envs,
env_keys,
timeout,
bundled,
available_tools
},
internal_variant;
Stdio {
cmd,
args,
@@ -187,12 +163,7 @@ where
D: Deserializer<'de>,
{
let remotes = Option::<Vec<RecipeExtensionConfigInternal>>::deserialize(deserializer)?;
Ok(remotes.map(|items| {
items
.into_iter()
.map(ExtensionConfig::from)
.collect::<Vec<_>>()
}))
Ok(remotes.map(|items| items.into_iter().map(ExtensionConfig::from).collect()))
}
#[cfg(test)]