Skip to content

Commit 1d0952c

Browse files
committed
Release v0.8.0
Signed-off-by: Phillip Sitbon <[email protected]>
2 parents 7f31061 + d2e3aa0 commit 1d0952c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2325
-2114
lines changed

.github/workflows/docker-publish.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,20 @@ on:
44
push:
55
branches: [ main, beta ]
66
tags: [ 'v*.*.*' ]
7+
paths:
8+
- 'magg/**'
9+
- 'pyproject.toml'
10+
- 'uv.lock'
11+
- 'magg.dockerfile'
12+
- 'compose.yaml'
713
pull_request:
814
branches: [ main, beta ]
15+
paths:
16+
- 'magg/**'
17+
- 'pyproject.toml'
18+
- 'uv.lock'
19+
- 'magg.dockerfile'
20+
- 'compose.yaml'
921
workflow_dispatch:
1022

1123
env:

.github/workflows/test.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,18 @@ name: Tests
33
on:
44
push:
55
branches: ['*']
6+
paths:
7+
- 'magg/**'
8+
- 'tests/**'
9+
- 'pyproject.toml'
10+
- 'uv.lock'
611
pull_request:
712
branches: ['*']
13+
paths:
14+
- 'magg/**'
15+
- 'tests/**'
16+
- 'pyproject.toml'
17+
- 'uv.lock'
818

919
jobs:
1020
test:

docs/index.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,86 @@ Check logs for:
842842
- Tool delegation errors
843843
- Configuration problems
844844

845+
### Tool Naming and Prefix System
846+
847+
#### Understanding Tool Names
848+
849+
Magg organizes tools from multiple servers using a prefix system. Every tool name follows this pattern:
850+
851+
```
852+
{prefix}{separator}{tool_name}
853+
```
854+
855+
Where:
856+
- `prefix` is the namespace for a server (configurable per server)
857+
- `separator` is configurable via `prefix_sep` field or `MAGG_PREFIX_SEP` env var (default: `_`)
858+
- `tool_name` is the original tool name from the server
859+
860+
Examples:
861+
- `calc_add` - "add" tool from server with "calc" prefix
862+
- `pw_screenshot` - "screenshot" tool from server with "pw" prefix
863+
- `magg_list_servers` - "list_servers" tool from Magg itself
864+
- `read_file` - "read_file" tool from server with no prefix
865+
866+
#### Configuring Server Prefixes
867+
868+
When adding a server, specify the `prefix` field:
869+
870+
```json
871+
{
872+
"name": "calculator",
873+
"source": "...",
874+
"prefix": "calc" // All tools will be calc_*
875+
}
876+
```
877+
878+
Special cases:
879+
- `"prefix": ""` or `"prefix": null` - No prefix, tools keep original names
880+
- Omitting prefix field - Server name is used as default prefix
881+
- Multiple servers can share the same prefix (tools are merged)
882+
883+
#### Configuring Magg's Prefix
884+
885+
Magg's own tools use the prefix from `MAGG_SELF_PREFIX`:
886+
887+
```bash
888+
# Default: magg_list_servers, magg_add_server, etc.
889+
export MAGG_SELF_PREFIX=magg
890+
891+
# Custom: myapp_list_servers, myapp_add_server, etc.
892+
export MAGG_SELF_PREFIX=myapp
893+
894+
# No prefix: list_servers, add_server, etc.
895+
export MAGG_SELF_PREFIX=""
896+
```
897+
898+
#### Configuring the Separator
899+
900+
You can change the separator between prefix and tool name:
901+
902+
```bash
903+
# Default underscore separator: calc_add, magg_list_servers
904+
export MAGG_PREFIX_SEP="_"
905+
906+
# Dash separator: calc-add, magg-list-servers
907+
export MAGG_PREFIX_SEP="-"
908+
909+
# Dot separator: calc.add, magg.list.servers
910+
export MAGG_PREFIX_SEP="."
911+
912+
# No separator: calcadd, magglistservers
913+
export MAGG_PREFIX_SEP=""
914+
```
915+
916+
#### Prefix Validation Rules
917+
918+
- Must be valid Python identifiers (alphanumeric, not starting with digit)
919+
- Cannot contain the separator character (default: underscore, but configurable)
920+
- Case-sensitive
921+
- Empty string allowed (results in no prefix)
922+
923+
Note: While the separator is configurable, server prefixes currently still cannot contain underscores due to Python identifier constraints.
924+
845925
### Performance Optimization
846926

847927
1. **Disable unused servers** to reduce memory usage

docs/proxy.md

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,45 @@ The proxy tool accepts the following parameters:
4040
- For resources: typically not used (URI is in path)
4141
- For prompts: prompt-specific arguments
4242

43+
- **limit** (optional): integer (1-1000)
44+
- Maximum number of items to return
45+
- Only allowed for `list` action
46+
- Default: 100
47+
48+
- **offset** (optional): integer (≥0)
49+
- Number of items to skip
50+
- Only allowed for `list` action
51+
- Default: 0
52+
53+
- **filter_server** (optional): string
54+
- Filter results by server name prefix
55+
- Only allowed for `list` action
56+
- Example: `"serena_"` to show only tools from the serena server
57+
4358
### Examples
4459

4560
```json
46-
// List all tools
61+
// List all tools (with default pagination: limit=100, offset=0)
4762
{
4863
"action": "list",
4964
"type": "tool"
5065
}
5166

67+
// List tools with pagination
68+
{
69+
"action": "list",
70+
"type": "tool",
71+
"limit": 50,
72+
"offset": 50
73+
}
74+
75+
// List tools from a specific server only
76+
{
77+
"action": "list",
78+
"type": "tool",
79+
"filter_server": "serena_"
80+
}
81+
5282
// Get info about a specific tool
5383
{
5484
"action": "info",
@@ -101,7 +131,10 @@ Example list response structure:
101131
proxyAction="list",
102132
proxyType="tool",
103133
pythonType="Tool", # Or "Resource | ResourceTemplate", "Prompt"
104-
many=True # For list actions
134+
many=True, # For list actions
135+
totalCount=250, # Total number of items available
136+
offset=0, # Current offset
137+
limit=100 # Current limit
105138
)
106139
)
107140
]
@@ -333,7 +366,8 @@ Proxy errors are returned as standard MCP errors:
333366

334367
- Self-client connection is reused across calls
335368
- Use targeted queries when possible
336-
- Consider implementing pagination for large result sets
369+
- Pagination is implemented with `limit`, `offset`, and `filter_server` parameters
370+
- Default limit is 100 items to prevent token limit issues
337371
- Transparent mode adds minimal overhead (one extra tool call)
338372

339373
## Future Extensions

examples/messaging.py

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,24 @@
1111

1212
class CustomMessageHandler(MaggMessageHandler):
1313
"""Custom message handler that logs all notifications."""
14-
14+
1515
def __init__(self):
1616
super().__init__()
1717
self.notification_count = 0
18-
18+
1919
async def on_message(self, message):
2020
"""Called for all messages."""
2121
self.notification_count += 1
2222
print(f"📥 Received message #{self.notification_count}: {type(message).__name__}")
23-
23+
2424
async def on_tool_list_changed(self, notification: mcp.types.ToolListChangedNotification):
2525
"""Called when tool list changes."""
2626
print("🔧 Tool list changed! Available tools may have been updated.")
27-
27+
2828
async def on_resource_list_changed(self, notification: mcp.types.ResourceListChangedNotification):
2929
"""Called when resource list changes."""
3030
print("📁 Resource list changed! Available resources may have been updated.")
31-
31+
3232
async def on_progress(self, notification: mcp.types.ProgressNotification):
3333
"""Called for progress updates."""
3434
if notification.params:
@@ -39,7 +39,7 @@ async def on_progress(self, notification: mcp.types.ProgressNotification):
3939
print(f"⏳ Progress: {progress}/{total} ({percentage:.1f}%)")
4040
else:
4141
print(f"⏳ Progress: {progress}")
42-
42+
4343
async def on_logging_message(self, notification: mcp.types.LoggingMessageNotification):
4444
"""Called for log messages from servers."""
4545
if notification.params:
@@ -51,38 +51,38 @@ async def on_logging_message(self, notification: mcp.types.LoggingMessageNotific
5151
async def callback_example():
5252
"""Example using callback-based message handler."""
5353
print("🚀 Starting callback-based message handler example...")
54-
54+
5555
def on_tool_change(notification):
5656
print("🔧 [Callback] Tools changed!")
57-
57+
5858
def on_progress(notification):
5959
if notification.params and notification.params.progress is not None:
6060
print(f"⏳ [Callback] Progress: {notification.params.progress}")
61-
61+
6262
# Create handler with callbacks
6363
handler = MaggMessageHandler(
6464
on_tool_list_changed=on_tool_change,
6565
on_progress=on_progress
6666
)
67-
67+
6868
# Create client with message handler
6969
client = MaggClient(
7070
"http://localhost:8000/mcp/", # MCP endpoint with trailing slash
7171
message_handler=handler
7272
)
73-
73+
7474
try:
7575
async with client:
7676
print("✅ Connected to Magg server with message handling")
77-
77+
7878
# List tools to see what's available
7979
tools = await client.list_tools()
8080
print(f"📋 Found {len(tools)} tools available")
81-
81+
8282
# Keep connection open to receive notifications
8383
print("👂 Listening for notifications... (press Ctrl+C to stop)")
8484
await asyncio.sleep(30) # Listen for 30 seconds
85-
85+
8686
except KeyboardInterrupt:
8787
print("\n🛑 Stopped listening for notifications")
8888
except Exception as e:
@@ -92,36 +92,36 @@ def on_progress(notification):
9292
async def class_example():
9393
"""Example using class-based message handler."""
9494
print("🚀 Starting class-based message handler example...")
95-
95+
9696
# Create custom handler
9797
handler = CustomMessageHandler()
98-
98+
9999
# Create client with message handler
100100
client = MaggClient(
101101
"http://localhost:8000/mcp/", # MCP endpoint with trailing slash
102102
message_handler=handler
103103
)
104-
104+
105105
try:
106106
async with client:
107107
print("✅ Connected to Magg server with custom message handler")
108-
108+
109109
# List available capabilities
110110
tools = await client.list_tools()
111111
resources = await client.list_resources()
112112
prompts = await client.list_prompts()
113-
113+
114114
print(f"📋 Available capabilities:")
115115
print(f" 🔧 Tools: {len(tools)}")
116116
print(f" 📁 Resources: {len(resources)}")
117117
print(f" 💬 Prompts: {len(prompts)}")
118-
118+
119119
# Keep connection open to receive notifications
120120
print("👂 Listening for notifications... (press Ctrl+C to stop)")
121121
await asyncio.sleep(30) # Listen for 30 seconds
122-
122+
123123
print(f"📊 Total notifications received: {handler.notification_count}")
124-
124+
125125
except KeyboardInterrupt:
126126
print("\n🛑 Stopped listening for notifications")
127127
except Exception as e:
@@ -136,17 +136,17 @@ async def main():
136136
print("This example demonstrates Magg's real-time messaging capabilities.")
137137
print("Make sure you have a Magg server running at http://localhost:8000")
138138
print()
139-
139+
140140
# Run callback example
141141
await callback_example()
142142
print()
143-
143+
144144
# Wait a bit between examples
145145
await asyncio.sleep(2)
146-
146+
147147
# Run class example
148148
await class_example()
149-
149+
150150
print()
151151
print("✨ Examples completed!")
152152
print()
@@ -157,4 +157,4 @@ async def main():
157157

158158

159159
if __name__ == "__main__":
160-
asyncio.run(main())
160+
asyncio.run(main())

magg/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
__all__ = [
1919
"MaggClient",
20-
"MaggMessageHandler",
20+
"MaggMessageHandler",
2121
"MessageRouter",
2222
"ServerMessageCoordinator",
2323
]

0 commit comments

Comments
 (0)