fix: allow concurrent tool execution within the same MCP extension (#7202)

Signed-off-by: rabi <ramishra@redhat.com>
This commit is contained in:
Rabi Mishra
2026-02-13 18:10:46 +05:30
committed by GitHub
parent 7c253e6b10
commit ad3bea3685
3 changed files with 45 additions and 103 deletions
+26 -68
View File
@@ -50,7 +50,7 @@ use rmcp::transport::auth::AuthClient;
use schemars::_private::NoSerialize;
use serde_json::Value;
type McpClientBox = Arc<Mutex<Box<dyn McpClientTrait>>>;
type McpClientBox = Arc<dyn McpClientTrait>;
static RE_ENV_BRACES: Lazy<regex::Regex> =
Lazy::new(|| regex::Regex::new(r"\$\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}").expect("valid regex"));
@@ -691,7 +691,7 @@ impl ExtensionManager {
let mut extensions = self.extensions.lock().await;
extensions.insert(
sanitized_name,
Extension::new(config, Arc::new(Mutex::new(client)), server_info, temp_dir),
Extension::new(config, Arc::from(client), server_info, temp_dir),
);
drop(extensions);
self.invalidate_tools_cache_and_bump_version().await;
@@ -862,8 +862,7 @@ impl ExtensionManager {
let ext_name = name.clone();
async move {
let mut tools = Vec::new();
let client_guard = client.lock().await;
let mut client_tools = match client_guard
let mut client_tools = match client
.list_tools(session_id, None, cancel_token.clone())
.await
{
@@ -913,7 +912,7 @@ impl ExtensionManager {
break;
}
client_tools = match client_guard
client_tools = match client
.list_tools(session_id, client_tools.next_cursor, cancel_token.clone())
.await
{
@@ -1065,8 +1064,7 @@ impl ExtensionManager {
.await
.ok_or(ErrorData::new(ErrorCode::INVALID_PARAMS, error_msg, None))?;
let client_guard = client.lock().await;
client_guard
client
.read_resource(session_id, uri, cancellation_token)
.await
.map_err(|_| {
@@ -1093,9 +1091,7 @@ impl ExtensionManager {
};
for (extension_name, client) in extensions_to_check {
let client_guard = client.lock().await;
match client_guard
match client
.list_resources(session_id, None, CancellationToken::default())
.await
{
@@ -1132,8 +1128,7 @@ impl ExtensionManager {
)
})?;
let client_guard = client.lock().await;
client_guard
client
.list_resources(session_id, None, cancellation_token)
.await
.map_err(|e| {
@@ -1307,7 +1302,7 @@ impl ExtensionManager {
let arguments = tool_call.arguments.clone();
let client = resolved.client.clone();
let notifications_receiver = client.lock().await.subscribe().await;
let notifications_receiver = client.subscribe().await;
let session_id = session_id.to_string();
let actual_tool_name = resolved.actual_tool_name;
let working_dir_str = working_dir.map(|p| p.to_string_lossy().to_string());
@@ -1319,8 +1314,7 @@ impl ExtensionManager {
session_id,
working_dir_str
);
let client_guard = client.lock().await;
client_guard
client
.call_tool(
&session_id,
&actual_tool_name,
@@ -1360,8 +1354,7 @@ impl ExtensionManager {
)
})?;
let client_guard = client.lock().await;
client_guard
client
.list_prompts(session_id, None, cancellation_token)
.await
.map_err(|e| {
@@ -1435,8 +1428,7 @@ impl ExtensionManager {
.await
.ok_or_else(|| anyhow::anyhow!("Extension {} not found", extension_name))?;
let client_guard = client.lock().await;
client_guard
client
.get_prompt(session_id, name, arguments, cancellation_token)
.await
.map_err(|e| anyhow::anyhow!("Failed to get prompt: {}", e))
@@ -1540,8 +1532,7 @@ impl ExtensionManager {
};
for (name, client) in platform_clients {
let client_guard = client.lock().await;
if let Some(moim_content) = client_guard.get_moim(session_id).await {
if let Some(moim_content) = client.get_moim(session_id).await {
tracing::debug!("MOIM content from {}: {} chars", name, moim_content.len());
content.push('\n');
content.push_str(&moim_content);
@@ -1707,24 +1698,15 @@ mod tests {
// Add some mock clients using the helper method
extension_manager
.add_mock_extension(
"test_client".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("test_client".to_string(), Arc::new(MockClient {}))
.await;
extension_manager
.add_mock_extension(
"__cli__ent__".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("__cli__ent__".to_string(), Arc::new(MockClient {}))
.await;
extension_manager
.add_mock_extension(
"client 🚀".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("client 🚀".to_string(), Arc::new(MockClient {}))
.await;
let tool_call = CallToolRequestParams {
@@ -1852,7 +1834,7 @@ mod tests {
extension_manager
.add_mock_extension_with_tools(
"test_extension".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
Arc::new(MockClient {}),
available_tools,
)
.await;
@@ -1882,7 +1864,7 @@ mod tests {
extension_manager
.add_mock_extension_with_tools(
"test_extension".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
Arc::new(MockClient {}),
vec![], // Empty available_tools means all tools are available by default
)
.await;
@@ -1914,7 +1896,7 @@ mod tests {
extension_manager
.add_mock_extension_with_tools(
"test_extension".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
Arc::new(MockClient {}),
available_tools,
)
.await;
@@ -2018,10 +2000,7 @@ mod tests {
ExtensionManager::new_without_provider(temp_dir.path().to_path_buf());
extension_manager
.add_mock_extension(
"ext_a".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("ext_a".to_string(), Arc::new(MockClient {}))
.await;
let tools_after_first = extension_manager
@@ -2036,10 +2015,7 @@ mod tests {
assert!(!tool_names.iter().any(|n| n.starts_with("ext_b__")));
extension_manager
.add_mock_extension(
"ext_b".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("ext_b".to_string(), Arc::new(MockClient {}))
.await;
let tools_after_second = extension_manager
@@ -2061,16 +2037,10 @@ mod tests {
ExtensionManager::new_without_provider(temp_dir.path().to_path_buf());
extension_manager
.add_mock_extension(
"ext_a".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("ext_a".to_string(), Arc::new(MockClient {}))
.await;
extension_manager
.add_mock_extension(
"ext_b".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("ext_b".to_string(), Arc::new(MockClient {}))
.await;
let tools_before = extension_manager
@@ -2099,16 +2069,10 @@ mod tests {
ExtensionManager::new_without_provider(temp_dir.path().to_path_buf());
extension_manager
.add_mock_extension(
"ext_a".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("ext_a".to_string(), Arc::new(MockClient {}))
.await;
extension_manager
.add_mock_extension(
"ext_b".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("ext_b".to_string(), Arc::new(MockClient {}))
.await;
let tools = extension_manager
@@ -2128,16 +2092,10 @@ mod tests {
ExtensionManager::new_without_provider(temp_dir.path().to_path_buf());
extension_manager
.add_mock_extension(
"ext_a".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("ext_a".to_string(), Arc::new(MockClient {}))
.await;
extension_manager
.add_mock_extension(
"ext_b".to_string(),
Arc::new(Mutex::new(Box::new(MockClient {}))),
)
.add_mock_extension("ext_b".to_string(), Arc::new(MockClient {}))
.await;
let tools = extension_manager