Skip to content

Commit 08d10d0

Browse files
committed
Add FastMCP messaging support and enhance MBro CLI multiline capabilities
## FastMCP Messaging & Notifications - Implement full support for FastMCP's bidirectional messaging (PR #939) - Add MaggMessageHandler with callback-based and class-based APIs - Create MessageRouter for multi-handler message distribution - Implement ServerMessageCoordinator for notification aggregation - Add BackendMessageHandler for per-server message forwarding - Update ProxyFastMCP with message routing infrastructure - Complete test suite with 8 passing messaging tests ## MBRO CLI Multiline Enhancements - Fix multiline input handling with Python REPL-style behavior - Add shell-like key=value argument parsing (e.g., name="test" count=42) - Enable native multiline mode with proper history support - Fix _create_smart_auto_suggest missing method error - Handle backslash line continuations correctly - Preserve multiline commands in history with newlines - Add custom Enter key binding for submit on empty line - Support editing multiline commands loaded from history ## Bug Fixes - Fix asyncio.get_event_loop() deprecation warning in logs/queue.py - Fix mbro CLI test expecting JSON error for shell-style args - Fix tool delegation test being too strict about tool count - Update terminal banner with dynamic width support ## Additional Improvements - Fix import issues and type hints for Python 3.13+ - Add comprehensive messaging documentation and examples - Update README with messaging feature documentation Signed-off-by: Phillip Sitbon <phillip.sitbon@gmail.com>
1 parent a12e378 commit 08d10d0

22 files changed

+1640
-173
lines changed

docs/index.md

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
7. [Prompts Reference](#prompts-reference)
1212
8. [Authentication](#authentication)
1313
9. [Configuration Reload](#configuration-reload)
14-
10. [Proxy Documentation](#proxy-documentation)
15-
11. [Example Sessions](#example-sessions)
16-
12. [Advanced Configuration](#advanced-configuration)
14+
10. [Messaging and Notifications](#messaging-and-notifications)
15+
11. [Proxy Documentation](#proxy-documentation)
16+
12. [Example Sessions](#example-sessions)
17+
13. [Advanced Configuration](#advanced-configuration)
1718

1819
## Overview
1920

@@ -539,6 +540,52 @@ result = await client.call_tool("magg_reload_config")
539540

540541
For complete documentation, see **[Configuration Reload Guide](config-reload.md)**.
541542

543+
## Messaging and Notifications
544+
545+
Magg provides comprehensive support for MCP messaging and notifications, enabling real-time communication between clients and backend servers. This feature leverages FastMCP's messaging capabilities to provide transparent message forwarding across multiple backend servers.
546+
547+
### Key Features
548+
- **Transparent Message Forwarding**: All notifications from backend servers are automatically forwarded to clients
549+
- **Message Aggregation**: Coordinates notifications from multiple backend servers
550+
- **Real-time Updates**: Receive immediate notifications for tool/resource/prompt list changes
551+
- **Progress Tracking**: Monitor long-running operations across all servers
552+
- **Custom Handlers**: Implement custom logic for different notification types
553+
554+
### Supported Notifications
555+
- **Tool List Changes**: When backend servers add/remove/modify tools
556+
- **Resource List Changes**: When available resources change
557+
- **Prompt List Changes**: When available prompts change
558+
- **Progress Updates**: For long-running operations
559+
- **Log Messages**: Server logs forwarded to clients
560+
- **Resource Updates**: When individual resources are modified
561+
562+
### Basic Usage
563+
564+
```python
565+
from magg import MaggClient, MaggMessageHandler
566+
567+
# Create message handler with callbacks
568+
handler = MaggMessageHandler(
569+
on_tool_list_changed=lambda notif: print("Tools changed!"),
570+
on_progress=lambda notif: print(f"Progress: {notif.progress}")
571+
)
572+
573+
# Create client with message handling
574+
client = MaggClient("http://localhost:8000", message_handler=handler)
575+
576+
async with client:
577+
# All notifications from backend servers automatically forwarded
578+
tools = await client.list_tools()
579+
```
580+
581+
### Advanced Features
582+
- **Server-specific handlers** for targeted message routing
583+
- **Message coordination** with deduplication and aggregation
584+
- **Custom handler classes** for complex notification logic
585+
- **Debug capabilities** for monitoring message flow
586+
587+
For complete documentation, see **[Messaging Guide](messaging.md)**.
588+
542589
## Proxy Documentation
543590

544591
Magg includes a powerful tool called `proxy` that provides tool-based access to all MCP capabilities. The proxy enables LLMs to interact with resources and prompts through a tool interface, making all MCP operations accessible even when the client doesn't support direct resource or prompt access.

docs/messaging.md

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
# MCP Messaging and Notifications
2+
3+
Magg provides comprehensive support for MCP messaging and notifications, enabling real-time communication between clients and backend servers. This feature leverages FastMCP's messaging capabilities to provide transparent message forwarding across multiple backend servers.
4+
5+
## Overview
6+
7+
The messaging system consists of several components:
8+
9+
1. **Client-side message handling** - MaggClient with MessageHandler support
10+
2. **Server-side message coordination** - ProxyFastMCP with message routing
11+
3. **Message forwarding** - Transparent proxy of notifications from backend servers
12+
4. **Message aggregation** - Coordination of notifications from multiple servers
13+
14+
## Client-Side Usage
15+
16+
### Basic Message Handling
17+
18+
```python
19+
from magg import MaggClient, MaggMessageHandler
20+
import mcp.types
21+
22+
# Create a custom message handler
23+
def on_tool_changed(notification: mcp.types.ToolListChangedNotification):
24+
print(f"Tools changed!")
25+
26+
def on_progress(notification: mcp.types.ProgressNotification):
27+
print(f"Progress: {notification.progress}/{notification.total}")
28+
29+
# Create handler with callbacks
30+
handler = MaggMessageHandler(
31+
on_tool_list_changed=on_tool_changed,
32+
on_progress=on_progress
33+
)
34+
35+
# Create client with message handling
36+
client = MaggClient(
37+
"http://localhost:8000",
38+
message_handler=handler
39+
)
40+
41+
async with client:
42+
# All notifications from backend servers will be forwarded to your handler
43+
tools = await client.list_tools()
44+
```
45+
46+
### Custom Message Handler Class
47+
48+
```python
49+
from magg.messaging import MaggMessageHandler
50+
import mcp.types
51+
52+
class MyMessageHandler(MaggMessageHandler):
53+
def __init__(self):
54+
super().__init__()
55+
self.tool_cache = []
56+
57+
async def on_tool_list_changed(
58+
self,
59+
notification: mcp.types.ToolListChangedNotification
60+
):
61+
print("Tool list changed - clearing cache")
62+
self.tool_cache.clear()
63+
64+
async def on_resource_list_changed(
65+
self,
66+
notification: mcp.types.ResourceListChangedNotification
67+
):
68+
print("Resource list changed")
69+
70+
async def on_progress(
71+
self,
72+
notification: mcp.types.ProgressNotification
73+
):
74+
print(f"Progress: {notification.progress}")
75+
76+
# Use custom handler
77+
handler = MyMessageHandler()
78+
client = MaggClient("http://localhost:8000", message_handler=handler)
79+
```
80+
81+
## Supported Notification Types
82+
83+
The messaging system supports all standard MCP notifications:
84+
85+
- **`ToolListChangedNotification`** - Tool inventory changes
86+
- **`ResourceListChangedNotification`** - Resource inventory changes
87+
- **`PromptListChangedNotification`** - Prompt inventory changes
88+
- **`ResourceUpdatedNotification`** - Individual resource updates
89+
- **`ProgressNotification`** - Progress updates from long-running operations
90+
- **`LoggingMessageNotification`** - Log messages from servers
91+
- **`CancelledNotification`** - Request cancellation notifications
92+
93+
## Server-Side Architecture
94+
95+
### Message Routing
96+
97+
Magg's server automatically sets up message forwarding when mounting backend servers:
98+
99+
```python
100+
# In ServerManager.mount_server():
101+
# 1. Create BackendMessageHandler for each server
102+
# 2. Connect Client with message_handler
103+
# 3. Mount server with ProxyFastMCP.mount_backend_server()
104+
# 4. All notifications automatically forwarded to clients
105+
```
106+
107+
### Message Coordination
108+
109+
The `ServerMessageCoordinator` handles:
110+
111+
- **Deduplication** - Prevents duplicate notifications
112+
- **Aggregation** - Combines notifications from multiple servers
113+
- **Routing** - Sends notifications to appropriate client handlers
114+
- **State tracking** - Maintains notification state for debugging
115+
116+
### Debugging Message Flow
117+
118+
```python
119+
# Access message coordinator for debugging
120+
coordinator = magg_server.mcp.message_coordinator
121+
state = await coordinator.get_notification_state()
122+
print(f"Servers with tool changes: {state.get('tool_changes', set())}")
123+
```
124+
125+
## Implementation Details
126+
127+
### Client Message Handler Registration
128+
129+
MaggClient automatically forwards the message handler to the underlying FastMCP Client:
130+
131+
```python
132+
# In MaggClient.__init__():
133+
super().__init__(
134+
transport,
135+
message_handler=message_handler, # Passed through to FastMCP
136+
# ... other args
137+
)
138+
```
139+
140+
### Backend Server Message Forwarding
141+
142+
Each backend server gets its own message handler:
143+
144+
```python
145+
# In ServerManager.mount_server():
146+
message_handler = BackendMessageHandler(
147+
server_id=server.name,
148+
coordinator=self.mcp.message_coordinator
149+
)
150+
client = Client(transport, message_handler=message_handler)
151+
```
152+
153+
### Message Flow
154+
155+
1. **Backend server** sends notification (e.g., tool list changed)
156+
2. **BackendMessageHandler** receives notification
157+
3. **ServerMessageCoordinator** processes and routes notification
158+
4. **MessageRouter** forwards to registered client handlers
159+
5. **Client MessageHandler** receives and processes notification
160+
161+
## Advanced Features
162+
163+
### Server-Specific Handlers
164+
165+
```python
166+
from magg.messaging import MessageRouter
167+
168+
router = MessageRouter()
169+
170+
# Register handler for specific server
171+
await router.register_handler(my_handler, server_id="weather-server")
172+
173+
# Register global handler for all servers
174+
await router.register_handler(global_handler, server_id=None)
175+
```
176+
177+
### Manual Notification Sending
178+
179+
From server-side tools using Context:
180+
181+
```python
182+
from fastmcp.server.context import Context
183+
184+
@server.tool
185+
async def my_tool(ctx: Context) -> str:
186+
# Do some work that changes available tools
187+
# ...
188+
189+
# Manually trigger tool list change notification
190+
await ctx.send_tool_list_changed()
191+
192+
return "Tools updated"
193+
```
194+
195+
## Error Handling
196+
197+
Message handlers should be robust:
198+
199+
```python
200+
class RobustMessageHandler(MaggMessageHandler):
201+
async def on_tool_list_changed(self, notification):
202+
try:
203+
# Handle notification
204+
await self.process_tool_change(notification)
205+
except Exception as e:
206+
logger.error(f"Error handling tool change: {e}")
207+
# Don't re-raise - prevents breaking message flow
208+
```
209+
210+
## Performance Considerations
211+
212+
- **Async handlers** - All message handlers are async to prevent blocking
213+
- **Concurrent processing** - Multiple handlers called concurrently with `asyncio.gather`
214+
- **Error isolation** - Handler exceptions don't affect other handlers or message flow
215+
- **Minimal overhead** - Message routing adds minimal latency to notifications
216+
217+
## Migration from Previous Versions
218+
219+
Existing Magg clients continue to work unchanged. To add message handling:
220+
221+
```python
222+
# Before
223+
client = MaggClient("http://localhost:8000")
224+
225+
# After
226+
handler = MaggMessageHandler(on_tool_list_changed=my_callback)
227+
client = MaggClient("http://localhost:8000", message_handler=handler)
228+
```
229+
230+
No server-side changes are required - message forwarding is automatically enabled for all mounted servers.

0 commit comments

Comments
 (0)