@@ -39,7 +39,7 @@ fn emit_tools_list_changed(event_tx: &broadcast::Sender<DomainEvent>, space_id:
3939// Helpers
4040// ---------------------------------------------------------------------------
4141
42- fn text_result ( v : Value ) -> CallToolResult {
42+ pub ( crate ) fn text_result ( v : Value ) -> CallToolResult {
4343 CallToolResult :: success ( vec ! [ Content :: text( v. to_string( ) ) ] )
4444}
4545
@@ -301,7 +301,7 @@ impl MetaTool for ListServersTool {
301301/// mutation. Returns the broker's decision so the caller can proceed only
302302/// on success. `mutate` is the thing that runs post-approval and is
303303/// expected to emit `tools/list_changed` when relevant.
304- async fn with_approval < F , Fut , T > (
304+ pub ( crate ) async fn with_approval < F , Fut , T > (
305305 call : & MetaToolCall < ' _ > ,
306306 tool_name : & ' static str ,
307307 summary : String ,
@@ -344,13 +344,11 @@ fn parse_string_arg(args: &Value, field: &str) -> Result<String, MetaToolError>
344344 . ok_or_else ( || MetaToolError :: InvalidArgument ( format ! ( "missing `{field}`" ) ) )
345345}
346346
347- /// Parse `scope` — only `"session"` is implemented in Phase 3 .
347+ /// Parse `scope` for enable/disable server tools .
348348fn parse_scope ( args : & Value ) -> Result < & ' static str , MetaToolError > {
349349 match args. get ( "scope" ) . and_then ( |v| v. as_str ( ) ) {
350350 None | Some ( "session" ) => Ok ( "session" ) ,
351- Some ( "workspace" ) => Err ( MetaToolError :: InvalidArgument (
352- "workspace scope not yet implemented; see Phase 4" . into ( ) ,
353- ) ) ,
351+ Some ( "workspace" ) => Ok ( "workspace" ) ,
354352 Some ( other) => Err ( MetaToolError :: InvalidArgument ( format ! (
355353 "invalid scope '{other}'; expected 'session' or 'workspace'"
356354 ) ) ) ,
@@ -408,9 +406,9 @@ impl MetaTool for EnableServerTool {
408406 }
409407
410408 fn description ( & self ) -> & ' static str {
411- "Enable an MCP server for the current session only. The server's tools \
412- appear on the next tools/list without changing workspace bindings. \
413- Use mcpmux_list_servers first to see current status ."
409+ "Enable an MCP server. Default scope is session (ephemeral). Use \
410+ scope: \" workspace \" to persist on the matched workspace binding \
411+ (requires approval). Use mcpmux_list_servers first."
414412 }
415413
416414 fn input_schema ( & self ) -> Value {
@@ -433,16 +431,22 @@ impl MetaTool for EnableServerTool {
433431 }
434432
435433 async fn call ( & self , call : MetaToolCall < ' _ > ) -> Result < CallToolResult , MetaToolError > {
436- parse_scope ( & call. args ) ?;
434+ let scope = parse_scope ( & call. args ) ?;
437435 let server_id = parse_string_arg ( & call. args , "server_id" ) ?;
438436 let space_id = caller_space_id ( & call) . await ?;
439437 validate_server_in_space ( & call, space_id, & server_id) . await ?;
438+
439+ if scope == "workspace" {
440+ return super :: workspace_server:: enable_workspace_server ( call, space_id, server_id)
441+ . await ;
442+ }
443+
440444 let session_id = require_session_id ( & call) ?;
441445
442446 if session_overrides_require_approval ( call. ctx ) . await {
443447 let overrides = call. ctx . session_overrides . clone ( ) ;
444448 let server_id_for_closure = server_id. clone ( ) ;
445- let session_id_owned = session_id. to_string ( ) ;
449+ let session_id_owned = session_id. clone ( ) ;
446450 let summary = format ! ( "Enable server '{server_id}' for this session" ) ;
447451 return with_approval (
448452 & call,
@@ -496,9 +500,10 @@ impl MetaTool for DisableServerTool {
496500 }
497501
498502 fn description ( & self ) -> & ' static str {
499- "Disable an MCP server for the current session only. Bound servers \
500- are muted until re-enabled or the session ends. Use \
501- mcpmux_list_servers to inspect status first."
503+ "Disable an MCP server. Default scope is session (ephemeral). Use \
504+ scope: \" workspace\" to remove the server-all layer from the \
505+ workspace binding (requires approval; custom FeatureSets must be \
506+ edited in the Workspaces UI)."
502507 }
503508
504509 fn input_schema ( & self ) -> Value {
@@ -521,16 +526,22 @@ impl MetaTool for DisableServerTool {
521526 }
522527
523528 async fn call ( & self , call : MetaToolCall < ' _ > ) -> Result < CallToolResult , MetaToolError > {
524- parse_scope ( & call. args ) ?;
529+ let scope = parse_scope ( & call. args ) ?;
525530 let server_id = parse_string_arg ( & call. args , "server_id" ) ?;
526531 let space_id = caller_space_id ( & call) . await ?;
527532 validate_server_in_space ( & call, space_id, & server_id) . await ?;
533+
534+ if scope == "workspace" {
535+ return super :: workspace_server:: disable_workspace_server ( call, space_id, server_id)
536+ . await ;
537+ }
538+
528539 let session_id = require_session_id ( & call) ?;
529540
530541 if session_overrides_require_approval ( call. ctx ) . await {
531542 let overrides = call. ctx . session_overrides . clone ( ) ;
532543 let server_id_for_closure = server_id. clone ( ) ;
533- let session_id_owned = session_id. to_string ( ) ;
544+ let session_id_owned = session_id. clone ( ) ;
534545 let summary = format ! ( "Disable server '{server_id}' for this session" ) ;
535546 return with_approval (
536547 & call,
0 commit comments