Skip to content

Commit 8bb4367

Browse files
committed
Move proxy to magg.proxy package, remove client.runner
- Create magg.proxy package with server/client/mixin modules - Remove client.runner - use FastMCPTransport directly - Rename ServerRunner to MAGGRunner with signal handling - Update fix_whitespace.py to walk directories itself Tests pass unchanged. Signed-off-by: Phillip Sitbon <phillip.sitbon@gmail.com>
1 parent b4a3b81 commit 8bb4367

28 files changed

+448
-657
lines changed

docs/proxy-spec.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
This specification defines the Model Context Protocol (MCP) Proxy Extension, which enables dynamic access to MCP capabilities through a single, unified tool interface. The proxy pattern allows MCP servers to aggregate, gateway, and dynamically expose capabilities from other servers without requiring clients to manage multiple connections.
1010

11+
This specification is based on the implementation in MAGG (MCP Aggregator) and serves as a reference for other MCP servers that want to implement similar proxy functionality. The reference implementation can be found in the `magg.proxy` package.
12+
1113
## 1. Introduction
1214

1315
The MCP Proxy Extension provides a standardized way for MCP servers to expose capabilities from other servers through a single tool. This enables powerful aggregation scenarios while maintaining the simplicity of the MCP protocol.
@@ -395,6 +397,21 @@ Response (tool result):
395397
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
396398
- [MAGG Implementation](https://github.com/sitbon/magg)
397399

400+
## 12. Reference Implementation
401+
402+
The reference implementation in MAGG includes:
403+
404+
1. **Server-side proxy** (`magg/proxy/server.py` and `magg/proxy/mixin.py`):
405+
- `ProxyFastMCP` wrapper class that adds proxy capabilities to FastMCP instances
406+
- `ProxyMCP` mixin class for servers that want built-in proxy support
407+
- Automatic tool registration and capability aggregation
408+
- Result transformation and annotation
409+
410+
2. **Client-side wrapper** (`magg/proxy/client.py`):
411+
- `ProxyClient` class that provides transparent access to proxied servers
412+
- Automatic result unwrapping and type conversion
413+
- Lazy connection management
414+
398415
## Appendix A: Future Considerations
399416

400417
### A.1 Batch Operations

docs/proxy.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ All call results include annotations with:
136136
MAGG provides a `ProxyClient` class that simplifies interaction with proxy-enabled servers:
137137

138138
```python
139-
from magg.client.proxy import ProxyClient
139+
from magg.proxy.client import ProxyClient
140140

141141
# Create a proxy-aware client
142142
async with ProxyClient("http://localhost:8080/mcp") as client:
@@ -205,7 +205,7 @@ All transparent methods handle result transformation automatically using the tra
205205

206206
## Server Implementation
207207

208-
MAGG's proxy server implementation (`ProxyMCP` mixin) provides:
208+
MAGG's proxy server implementation provides:
209209

210210
1. **Self-introspection**: Server can list its own capabilities via FastMCPTransport
211211
2. **Result transformation**: Automatic conversion between MCP types
@@ -214,12 +214,26 @@ MAGG's proxy server implementation (`ProxyMCP` mixin) provides:
214214

215215
### Key Classes
216216

217+
#### `ProxyFastMCP`
218+
219+
A wrapper class that adds proxy functionality to FastMCP instances:
220+
221+
```python
222+
from magg.proxy.server import ProxyFastMCP
223+
from fastmcp import FastMCP
224+
225+
# Wrap an existing FastMCP instance
226+
mcp = FastMCP(name="my-server")
227+
proxy_mcp = ProxyFastMCP(mcp)
228+
```
229+
217230
#### `ProxyMCP`
218231

219-
A mixin class that adds proxy functionality to MCP servers:
232+
A mixin class that servers can inherit from:
220233

221234
```python
222-
from magg.server.proxy import ProxyMCP, ManagedServer
235+
from magg.proxy.mixin import ProxyMCP
236+
from magg.server.manager import ManagedServer
223237

224238
class MyAggregator(ManagedServer, ProxyMCP):
225239
def __init__(self):
@@ -233,7 +247,7 @@ class MyAggregator(ManagedServer, ProxyMCP):
233247
Metadata extracted from proxy response annotations:
234248

235249
```python
236-
from magg.server.proxy import ProxyResponseInfo
250+
from magg.proxy.server import ProxyResponseInfo
237251

238252
# Extract metadata from annotations
239253
info = ProxyResponseInfo.from_annotations(result.annotations)
@@ -275,7 +289,7 @@ MAGG provides transform utilities for working with proxy results:
275289
from magg.util.transform import (
276290
tool_result_as_prompt_result,
277291
tool_result_as_resource_result,
278-
embedded_resource_python_object,
292+
get_embedded_resource_python_object,
279293
deserialize_embedded_resource_python_object
280294
)
281295

@@ -286,7 +300,7 @@ prompt_result = tool_result_as_prompt_result(tool_result)
286300
resource_result = tool_result_as_resource_result(tool_result)
287301

288302
# Get metadata from embedded resource
289-
python_type, json_data, many = embedded_resource_python_object(embedded_resource)
303+
python_type, json_data, many = get_embedded_resource_python_object(embedded_resource)
290304

291305
# Deserialize to proper MCP types
292306
obj = deserialize_embedded_resource_python_object(

magg/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@
88
__version__ = metadata.version("magg")
99
except metadata.PackageNotFoundError:
1010
__version__ = "unknown"
11+
12+
del metadata

magg/cli.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from . import __version__, process
1212
from .settings import ConfigManager, ServerConfig
1313
from .util.terminal import (
14-
print_success, print_error, print_warning,
14+
print_success, print_error, print_warning, print_startup_banner,
1515
print_info, print_server_list, print_status_summary, confirm_action
1616
)
1717

@@ -23,15 +23,14 @@
2323

2424
async def cmd_serve(args) -> None:
2525
"""Start MAGG server."""
26-
from magg.server.runner import print_startup_banner
27-
from magg.server.runner import ServerRunner
26+
from magg.server.runner import MAGGRunner
2827

2928
logger.info("Starting MAGG server (mode: %s)", 'http' if args.http else 'stdio')
3029

3130
if args.http:
3231
print_startup_banner()
3332

34-
runner = ServerRunner(args.config)
33+
runner = MAGGRunner(args.config)
3534

3635
if args.http:
3736
logger.info("Starting HTTP server on %s:%s", args.host, args.port)
@@ -363,7 +362,7 @@ def main():
363362
sys.exit(130) # Standard exit code for SIGINT
364363
except Exception as e:
365364
print_error(f"Unexpected error: {e}")
366-
if os.getenv('MAGG_DEBUG').lower() in {'1', 'true', 'yes'}:
365+
if os.getenv('MAGG_DEBUG', '').lower() in {'1', 'true', 'yes'}:
367366
import traceback
368367
traceback.print_exc()
369368
sys.exit(1)

magg/client/__init__.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,2 @@
11
"""Client utilities for running MAGG alongside user applications.
22
"""
3-
# from .runner import MAGGRunner, ConnectionInfo
4-
# from .proxy import ProxyClient
5-
#
6-
# __all__ = [
7-
# "MAGGRunner",
8-
# "ConnectionInfo",
9-
# "ProxyClient",
10-
# ]

0 commit comments

Comments
 (0)