@@ -11,13 +11,13 @@ use std::str::FromStr;
11
11
use serde_json:: json;
12
12
use sha2:: { Digest , Sha256 } ;
13
13
use std:: collections:: HashMap ;
14
- use std:: sync:: Arc ;
14
+ use std:: sync:: { Arc , Mutex } ;
15
15
use tokio:: sync:: RwLock ;
16
16
17
17
#[ derive( Clone ) ]
18
18
pub struct PluginService {
19
19
config : Config ,
20
- plugins : Arc < RwLock < HashMap < String , Plugin > > > ,
20
+ plugins : Arc < RwLock < HashMap < String , Arc < Mutex < Plugin > > > > > ,
21
21
tool_plugin_map : Arc < RwLock < HashMap < String , String > > > ,
22
22
oci_downloader : Arc < OciDownloader > ,
23
23
}
@@ -122,11 +122,18 @@ impl PluginService {
122
122
}
123
123
}
124
124
}
125
- let mut plugin = Plugin :: new ( & manifest, [ ] , true ) . unwrap ( ) ;
125
+ let plugin = Arc :: new ( Mutex :: new ( Plugin :: new ( & manifest, [ ] , true ) . unwrap ( ) ) ) ;
126
+ let plugin_clone = Arc :: clone ( & plugin) ;
126
127
127
128
// Try to get tool information from the plugin and populate the cache
128
- if let Ok ( result) = plugin. call :: < & str , & str > ( "describe" , "" ) {
129
- if let Ok ( parsed) = serde_json:: from_str :: < ListToolsResult > ( result) {
129
+ let describe_result = tokio:: task:: spawn_blocking ( move || {
130
+ let mut plugin = plugin_clone. lock ( ) . unwrap ( ) ;
131
+ plugin. call :: < & str , String > ( "describe" , "" )
132
+ } )
133
+ . await ;
134
+
135
+ if let Ok ( Ok ( result) ) = describe_result {
136
+ if let Ok ( parsed) = serde_json:: from_str :: < ListToolsResult > ( & result) {
130
137
let mut cache = self . tool_plugin_map . write ( ) . await ;
131
138
let skip_tools = plugin_cfg
132
139
. runtime_config
@@ -189,7 +196,7 @@ impl ServerHandler for PluginService {
189
196
request : CallToolRequestParam ,
190
197
_context : RequestContext < RoleServer > ,
191
198
) -> Result < CallToolResult , McpError > {
192
- let mut plugins = self . plugins . write ( ) . await ;
199
+ let plugins = self . plugins . read ( ) . await ;
193
200
let tool_cache = self . tool_plugin_map . read ( ) . await ;
194
201
195
202
let tool_name = request. name . clone ( ) ;
@@ -201,23 +208,35 @@ impl ServerHandler for PluginService {
201
208
202
209
// Check if the tool exists in the cache
203
210
if let Some ( plugin_name) = tool_cache. get ( & tool_name. to_string ( ) ) {
204
- if let Some ( plugin) = plugins. get_mut ( plugin_name) {
205
- return match plugin. call :: < & str , & str > ( "call" , & json_string) {
206
- Ok ( result) => match serde_json:: from_str :: < CallToolResult > ( result) {
211
+ if let Some ( plugin_arc) = plugins. get ( plugin_name) {
212
+ let plugin_clone = Arc :: clone ( plugin_arc) ;
213
+ let plugin_name_clone = plugin_name. clone ( ) ;
214
+
215
+ let result = tokio:: task:: spawn_blocking ( move || {
216
+ let mut plugin = plugin_clone. lock ( ) . unwrap ( ) ;
217
+ plugin. call :: < & str , String > ( "call" , & json_string)
218
+ } )
219
+ . await ;
220
+
221
+ return match result {
222
+ Ok ( Ok ( result) ) => match serde_json:: from_str :: < CallToolResult > ( & result) {
207
223
Ok ( parsed) => Ok ( parsed) ,
208
- Err ( e) => {
209
- return Err ( McpError :: internal_error (
210
- format ! ( "Failed to deserialize data: {}" , e) ,
211
- None ,
212
- ) ) ;
213
- }
214
- } ,
215
- Err ( e) => {
216
- return Err ( McpError :: internal_error (
217
- format ! ( "Failed to execute plugin {}: {}" , plugin_name, e) ,
224
+ Err ( e) => Err ( McpError :: internal_error (
225
+ format ! ( "Failed to deserialize data: {}" , e) ,
218
226
None ,
219
- ) ) ;
220
- }
227
+ ) ) ,
228
+ } ,
229
+ Ok ( Err ( e) ) => Err ( McpError :: internal_error (
230
+ format ! ( "Failed to execute plugin {}: {}" , plugin_name_clone, e) ,
231
+ None ,
232
+ ) ) ,
233
+ Err ( e) => Err ( McpError :: internal_error (
234
+ format ! (
235
+ "Failed to spawn blocking task for plugin {}: {}" ,
236
+ plugin_name_clone, e
237
+ ) ,
238
+ None ,
239
+ ) ) ,
221
240
} ;
222
241
}
223
242
}
@@ -231,7 +250,7 @@ impl ServerHandler for PluginService {
231
250
_context : RequestContext < RoleServer > ,
232
251
) -> std:: result:: Result < ListToolsResult , McpError > {
233
252
tracing:: info!( "got tools/list request {:?}" , request) ;
234
- let mut plugins = self . plugins . write ( ) . await ;
253
+ let plugins = self . plugins . write ( ) . await ;
235
254
let mut tool_cache = self . tool_plugin_map . write ( ) . await ;
236
255
237
256
let mut payload = ListToolsResult :: default ( ) ;
@@ -240,10 +259,19 @@ impl ServerHandler for PluginService {
240
259
tool_cache. clear ( ) ;
241
260
242
261
for plugin_cfg in & self . config . plugins {
243
- if let Some ( plugin) = plugins. get_mut ( & plugin_cfg. name ) {
244
- match plugin. call :: < & str , & str > ( "describe" , "" ) {
245
- Ok ( result) => {
246
- if let Ok ( parsed) = serde_json:: from_str :: < ListToolsResult > ( result) {
262
+ if let Some ( plugin_arc) = plugins. get ( & plugin_cfg. name ) {
263
+ let plugin_clone = Arc :: clone ( plugin_arc) ;
264
+ let plugin_name = plugin_cfg. name . clone ( ) ;
265
+
266
+ let result = tokio:: task:: spawn_blocking ( move || {
267
+ let mut plugin = plugin_clone. lock ( ) . unwrap ( ) ;
268
+ plugin. call :: < & str , String > ( "describe" , "" )
269
+ } )
270
+ . await ;
271
+
272
+ match result {
273
+ Ok ( Ok ( result) ) => {
274
+ if let Ok ( parsed) = serde_json:: from_str :: < ListToolsResult > ( & result) {
247
275
let skip_tools = plugin_cfg
248
276
. runtime_config
249
277
. as_ref ( )
@@ -262,8 +290,11 @@ impl ServerHandler for PluginService {
262
290
}
263
291
}
264
292
}
293
+ Ok ( Err ( e) ) => {
294
+ log:: error!( "tool {} describe() error: {}" , plugin_name, e) ;
295
+ }
265
296
Err ( e) => {
266
- log:: error!( "tool {} describe() error: {}" , plugin_cfg . name , e) ;
297
+ log:: error!( "tool {} spawn_blocking error: {}" , plugin_name , e) ;
267
298
}
268
299
}
269
300
}
0 commit comments