@@ -42,19 +42,29 @@ async def find_available_port(preferred_port, max_attempts=10):
4242 return preferred_port
4343
4444
45- async def run_profile_fastmcp (profile_servers , profile_name , http_mode = False , port = DEFAULT_PORT , host = "127.0.0.1" ):
45+ async def run_profile_fastmcp (
46+ profile_servers , profile_name , http_mode = False , sse_mode = False , port = DEFAULT_PORT , host = "127.0.0.1"
47+ ):
4648 """Run profile servers using FastMCP proxy for proper aggregation."""
4749 server_count = len (profile_servers )
4850 logger .debug (f"Using FastMCP proxy to aggregate { server_count } server(s)" )
49- logger .debug (f"Mode: { 'HTTP' if http_mode else 'stdio' } " )
51+ mode = "SSE" if sse_mode else "HTTP" if http_mode else "stdio"
52+ logger .debug (f"Mode: { mode } " )
5053
5154 try :
5255 # Create FastMCP proxy for profile servers
56+ if sse_mode :
57+ action = "profile_run_sse"
58+ elif http_mode :
59+ action = "profile_run_http"
60+ else :
61+ action = "profile_run"
62+
5363 proxy = await create_mcpm_proxy (
5464 servers = profile_servers ,
5565 name = f"profile-{ profile_name } " ,
56- stdio_mode = not http_mode , # stdio_mode=False for HTTP
57- action = "profile_run" ,
66+ stdio_mode = not ( http_mode or sse_mode ) , # stdio_mode=False for HTTP/SSE
67+ action = action ,
5868 profile_name = profile_name ,
5969 )
6070
@@ -68,34 +78,41 @@ async def run_profile_fastmcp(profile_servers, profile_name, http_mode=False, po
6878
6979 # Note: Usage tracking is handled by proxy middleware
7080
71- if http_mode :
81+ if http_mode or sse_mode :
7282 # Try to find an available port if the requested one is taken
7383 actual_port = await find_available_port (port )
7484 if actual_port != port :
7585 logger .debug (f"Port { port } is busy, using port { actual_port } instead" )
7686
7787 # Display profile information in a nice panel
78- http_url = f"http://{ host } :{ actual_port } /mcp/"
88+ if sse_mode :
89+ server_url = f"http://{ host } :{ actual_port } /sse/"
90+ title = "📡 SSE Profile Running"
91+ else :
92+ server_url = f"http://{ host } :{ actual_port } /mcp/"
93+ title = "📁 Profile Running Locally"
7994
8095 # Build server list
8196 server_list = "\n " .join ([f" • [cyan]{ server .name } [/]" for server in profile_servers ])
8297
83- panel_content = f"[bold]Profile:[/] { profile_name } \n [bold]URL:[/] [cyan]{ http_url } [/cyan]\n \n [bold]Servers:[/]\n { server_list } \n \n [dim]Press Ctrl+C to stop the profile[/]"
98+ panel_content = f"[bold]Profile:[/] { profile_name } \n [bold]URL:[/] [cyan]{ server_url } [/cyan]\n \n [bold]Servers:[/]\n { server_list } \n \n [dim]Press Ctrl+C to stop the profile[/]"
8499
85100 panel = Panel (
86101 panel_content ,
87- title = "📁 Profile Running Locally" ,
102+ title = title ,
88103 title_align = "left" ,
89104 border_style = "green" ,
90105 padding = (1 , 2 ),
91106 )
92107 console .print (panel )
93108
94- logger .debug (f"Starting FastMCP proxy for profile '{ profile_name } ' on { host } :{ actual_port } " )
109+ mode = "SSE" if sse_mode else "HTTP"
110+ logger .debug (f"Starting FastMCP proxy for profile '{ profile_name } ' in { mode } mode on { host } :{ actual_port } " )
95111
96- # Run the aggregated proxy over HTTP with uvicorn logging control
112+ # Run the aggregated proxy over HTTP/SSE with uvicorn logging control
113+ transport = "sse" if sse_mode else "http"
97114 await proxy .run_http_async (
98- host = host , port = actual_port , uvicorn_config = {"log_level" : get_uvicorn_log_level ()}
115+ host = host , port = actual_port , transport = transport , uvicorn_config = {"log_level" : get_uvicorn_log_level ()}
99116 )
100117 else :
101118 # Run the aggregated proxy over stdio (default)
@@ -106,6 +123,8 @@ async def run_profile_fastmcp(profile_servers, profile_name, http_mode=False, po
106123
107124 except KeyboardInterrupt :
108125 logger .info ("Profile execution interrupted" )
126+ if http_mode or sse_mode :
127+ logger .warning ("\n Profile execution interrupted" )
109128 return 130
110129 except Exception as e :
111130 logger .error (f"Error running profile '{ profile_name } ': { e } " )
@@ -115,11 +134,12 @@ async def run_profile_fastmcp(profile_servers, profile_name, http_mode=False, po
115134@click .command ()
116135@click .argument ("profile_name" )
117136@click .option ("--http" , is_flag = True , help = "Run profile over HTTP instead of stdio" )
118- @click .option ("--port" , type = int , default = DEFAULT_PORT , help = f"Port for HTTP mode (default: { DEFAULT_PORT } )" )
119- @click .option ("--host" , type = str , default = "127.0.0.1" , help = "Host address for HTTP mode (default: 127.0.0.1)" )
137+ @click .option ("--sse" , is_flag = True , help = "Run profile over SSE instead of stdio" )
138+ @click .option ("--port" , type = int , default = DEFAULT_PORT , help = f"Port for HTTP / SSE mode (default: { DEFAULT_PORT } )" )
139+ @click .option ("--host" , type = str , default = "127.0.0.1" , help = "Host address for HTTP / SSE mode (default: 127.0.0.1)" )
120140@click .help_option ("-h" , "--help" )
121- def run (profile_name , http , port , host ):
122- """Execute all servers in a profile over stdio or HTTP .
141+ def run (profile_name , http , sse , port , host ):
142+ """Execute all servers in a profile over stdio, HTTP, or SSE .
123143
124144 Uses FastMCP proxy to aggregate servers into a unified MCP interface
125145 with proper capability namespacing. By default runs over stdio.
@@ -129,7 +149,9 @@ def run(profile_name, http, port, host):
129149 \b
130150 mcpm profile run web-dev # Run over stdio (default)
131151 mcpm profile run --http web-dev # Run over HTTP on 127.0.0.1:6276
152+ mcpm profile run --sse web-dev # Run over SSE on 127.0.0.1:6276
132153 mcpm profile run --http --port 9000 ai # Run over HTTP on 127.0.0.1:9000
154+ mcpm profile run --sse --port 9000 ai # Run over SSE on 127.0.0.1:9000
133155 mcpm profile run --http --host 0.0.0.0 web-dev # Run over HTTP on 0.0.0.0:6276
134156
135157 Debug logging: Set MCPM_DEBUG=1 for verbose output
@@ -141,6 +163,11 @@ def run(profile_name, http, port, host):
141163
142164 profile_name = profile_name .strip ()
143165
166+ # Validate mutually exclusive options
167+ if http and sse :
168+ logger .error ("Error: Cannot use both --http and --sse flags together" )
169+ return 1
170+
144171 # Check if profile exists
145172 try :
146173 profile_servers = profile_config_manager .get_profile (profile_name )
@@ -169,8 +196,12 @@ def run(profile_name, http, port, host):
169196
170197 # Use FastMCP proxy for all cases (single or multiple servers)
171198 logger .debug (f"Using FastMCP proxy for { len (profile_servers )} server(s)" )
172- if http :
173- logger .debug (f"HTTP mode on port { port } " )
199+ mode = "SSE" if sse else "HTTP" if http else "stdio"
200+ logger .debug (f"Mode: { mode } " )
201+ if http or sse :
202+ logger .debug (f"Port: { port } " )
174203
175204 # Run the async function
176- return asyncio .run (run_profile_fastmcp (profile_servers , profile_name , http_mode = http , port = port , host = host ))
205+ return asyncio .run (
206+ run_profile_fastmcp (profile_servers , profile_name , http_mode = http , sse_mode = sse , port = port , host = host )
207+ )
0 commit comments