Skip to content

Commit fcac5df

Browse files
committed
feat(meta-tools): target any Space via space_id + add mcpmux_list_spaces
Make the self-management surface flexible: a client can now inspect and configure any Space it can discover, with user approval as the gate. - New read tool `mcpmux_list_spaces` → [{id, name, is_default, description}] so the LLM can discover valid space ids. - Optional `space_id` on every tool (list_all_tools, search_tools, list_feature_sets, manage_feature_set, bind_current_workspace). Omitted ⇒ the caller''s resolved Space (back-compatible); provided ⇒ that Space (validated to exist). bind can now route a workspace into a *different* Space. - Approval payload carries `space_name`; every write summary names the target Space so a cross-Space change is a conscious approval, not a surprise. - Reads stay silent (local-trust); writes still prompt. Advertise a trigger language so the LLM isolates McpMux requests from the user''s actual work: the server `instructions` now teach the `@mux` convention and that `mcpmux_*` are McpMux controls, not task tools. Built-in descriptor lists mcpmux_list_spaces. Tests: list_spaces, unknown-space rejection, cross-Space create + bind land in the targeted Space (not the caller''s), updated advertise + parity guards. Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
1 parent 43652ea commit fcac5df

6 files changed

Lines changed: 339 additions & 51 deletions

File tree

crates/mcpmux-core/src/domain/builtin.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,14 @@ pub fn builtin_servers() -> Vec<BuiltinServerDescriptor> {
5656
each with your approval. Reads are silent; writes need approval.",
5757
default_enabled: true,
5858
tools: vec![
59+
BuiltinToolDescriptor {
60+
name: "mcpmux_list_spaces",
61+
description: "List all Spaces so the AI can target one by id.",
62+
write: false,
63+
},
5964
BuiltinToolDescriptor {
6065
name: "mcpmux_list_all_tools",
61-
description: "Browse every tool available in the resolved Space, unfiltered.",
66+
description: "Browse every tool available in a Space, unfiltered.",
6267
write: false,
6368
},
6469
BuiltinToolDescriptor {

crates/mcpmux-gateway/src/mcp/handler.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,21 @@ impl ServerHandler for McpMuxGatewayHandler {
354354
info.server_info = server_info;
355355
info.instructions = Some(
356356
"McpMux aggregates multiple MCP servers. Use tools/prompts/resources \
357-
from your authorized backend servers."
357+
from your authorized backend servers to do the user's work.\n\n\
358+
The `mcpmux_*` tools are different: they are McpMux's own \
359+
self-management / tool-optimization controls, NOT tools for the \
360+
user's task. Use them ONLY when the user is explicitly managing \
361+
their McpMux setup — discovering available tools, composing or \
362+
editing FeatureSets, listing Spaces, or routing (binding) a \
363+
workspace to a FeatureSet. The trigger word is `@mux`: when a user \
364+
message contains `@mux` (e.g. \"@mux build a minimal toolset for \
365+
this repo\"), treat it as a McpMux tool-optimization request and use \
366+
the `mcpmux_*` tools to fulfill it. Otherwise do not call them. \
367+
Reads (mcpmux_list_spaces / list_all_tools / search_tools / \
368+
list_feature_sets) are safe to call freely once the user has opted \
369+
in; writes (manage_feature_set, bind_current_workspace) prompt the \
370+
user for approval. Most operations accept an optional `space_id` \
371+
(from mcpmux_list_spaces) to target a specific Space."
358372
.to_string(),
359373
);
360374
info

crates/mcpmux-gateway/src/services/meta_tools/approval.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ pub struct ApprovalPayload {
7373
/// Human summary the dialog puts above the diff. e.g.
7474
/// "Pin this connection to FeatureSet 'android-dev' (12 tools)".
7575
pub summary: String,
76+
/// Name of the Space this write targets, surfaced as a labeled chip so the
77+
/// user can see (and reject) a change aimed at a Space other than the one
78+
/// they expect — important now that a client may pass any `space_id`.
79+
/// `None` for writes with no single target Space.
80+
#[serde(default, skip_serializing_if = "Option::is_none")]
81+
pub space_name: Option<String>,
7682
/// Tool-list diff the dialog shows to make the change concrete.
7783
/// Optional because some writes (e.g. create_feature_set without
7884
/// activation) don't shift the caller's resolved toolset.
@@ -350,6 +356,7 @@ mod tests {
350356
ApprovalPayload {
351357
tool_name: "mcpmux_pin_this_session".into(),
352358
summary: "test".into(),
359+
space_name: None,
353360
diff: None,
354361
raw_args: serde_json::json!({}),
355362
affects_other_clients: false,

crates/mcpmux-gateway/src/services/meta_tools/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ pub fn build_default_registry(
7777

7878
let mut registry = MetaToolRegistry::new(ctx);
7979
// Reads — no approval needed.
80+
registry.register(Box::new(tools::ListSpacesTool));
8081
registry.register(Box::new(tools::ListAllToolsTool));
8182
registry.register(Box::new(tools::SearchToolsTool));
8283
registry.register(Box::new(tools::ListFeatureSetsTool));

0 commit comments

Comments
 (0)