mirror of
				https://github.com/open-webui/mcpo
				synced 2025-06-26 18:26:58 +00:00 
			
		
		
		
	
						commit
						3b5f4fe17e
					
				| @ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. | ||||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), | ||||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||||
| 
 | ||||
| ## [0.0.11] - 2025-04-12 | ||||
| 
 | ||||
| ### Added | ||||
| 
 | ||||
| - 🌊 **SSE-Based MCP Server Support**: mcpo now supports SSE (Server-Sent Events) MCP servers out of the box—just pass 'mcpo --server-type "sse" -- http://127.0.0.1:8001/sse' when launching or use the standard "url" field in your config for seamless real-time integration with streaming MCP endpoints; see the README for full examples and enhanced workflows with live progress, event pushes, and interactive updates. | ||||
| 
 | ||||
| ## [0.0.10] - 2025-04-10 | ||||
| 
 | ||||
| ### Added | ||||
|  | ||||
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
								
							| @ -40,6 +40,12 @@ pip install mcpo | ||||
| mcpo --port 8000 --api-key "top-secret" -- your_mcp_server_command | ||||
| ``` | ||||
| 
 | ||||
| To use an SSE-compatible MCP server, simply specify the server type and endpoint: | ||||
| 
 | ||||
| ```bash | ||||
| mcpo --port 8000 --api-key "top-secret" --server-type "sse" -- http://127.0.0.1:8001/sse | ||||
| ``` | ||||
| 
 | ||||
| You can also run mcpo via Docker with no installation: | ||||
| 
 | ||||
| ```bash | ||||
| @ -78,7 +84,10 @@ Example config.json: | ||||
|     "time": { | ||||
|       "command": "uvx", | ||||
|       "args": ["mcp-server-time", "--local-timezone=America/New_York"] | ||||
|     } | ||||
|     }, | ||||
|     "mcp_sse": { | ||||
|       "url": "http://127.0.0.1:8001/sse" | ||||
|     } // SSE MCP Server | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| @ -110,7 +119,7 @@ To contribute or run tests locally: | ||||
| 
 | ||||
| 2.  **Run tests:** | ||||
|     ```bash | ||||
|     pytest | ||||
|     uv run pytest | ||||
|     ``` | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| [project] | ||||
| name = "mcpo" | ||||
| version = "0.0.10" | ||||
| version = "0.0.11" | ||||
| description = "A simple, secure MCP-to-OpenAPI proxy server" | ||||
| authors = [ | ||||
|     { name = "Timothy Jaeryang Baek", email = "tim@openwebui.com" } | ||||
|  | ||||
| @ -33,7 +33,10 @@ def main( | ||||
|         Optional[str], | ||||
|         typer.Option("--env-path", help="Path to environment variables file"), | ||||
|     ] = None, | ||||
|     config: Annotated[ | ||||
|     server_type: Annotated[ | ||||
|         Optional[str], typer.Option("--type", "--server-type", help="Server type") | ||||
|     ] = "stdio", | ||||
|     config_path: Annotated[ | ||||
|         Optional[str], typer.Option("--config", "-c", help="Config file path") | ||||
|     ] = None, | ||||
|     name: Annotated[ | ||||
| @ -56,7 +59,7 @@ def main( | ||||
|     ] = None, | ||||
| ): | ||||
|     server_command = None | ||||
|     if not config: | ||||
|     if not config_path: | ||||
|         # Find the position of "--" | ||||
|         if "--" not in sys.argv: | ||||
|             typer.echo("Usage: mcpo --host 0.0.0.0 --port 8000 -- your_mcp_command") | ||||
| @ -71,8 +74,8 @@ def main( | ||||
| 
 | ||||
|     from mcpo.main import run | ||||
| 
 | ||||
|     if config: | ||||
|         print("Starting MCP OpenAPI Proxy with config file:", config) | ||||
|     if config_path: | ||||
|         print("Starting MCP OpenAPI Proxy with config file:", config_path) | ||||
|     else: | ||||
|         print( | ||||
|             f"Starting MCP OpenAPI Proxy on {host}:{port} with command: {' '.join(server_command)}" | ||||
| @ -114,7 +117,8 @@ def main( | ||||
|             port, | ||||
|             api_key=api_key, | ||||
|             cors_allow_origins=cors_allow_origins, | ||||
|             config=config, | ||||
|             server_type=server_type, | ||||
|             config_path=config_path, | ||||
|             name=name, | ||||
|             description=description, | ||||
|             version=version, | ||||
|  | ||||
| @ -11,7 +11,7 @@ from starlette.routing import Mount | ||||
| 
 | ||||
| from mcp import ClientSession, StdioServerParameters | ||||
| from mcp.client.stdio import stdio_client | ||||
| 
 | ||||
| from mcp.client.sse import sse_client | ||||
| 
 | ||||
| from mcpo.utils.main import get_model_fields, get_tool_handler | ||||
| from mcpo.utils.auth import get_verify_api_key | ||||
| @ -65,13 +65,18 @@ async def create_dynamic_endpoints(app: FastAPI, api_dependency=None): | ||||
| 
 | ||||
| @asynccontextmanager | ||||
| async def lifespan(app: FastAPI): | ||||
|     server_type = getattr(app.state, "server_type", "stdio") | ||||
|     command = getattr(app.state, "command", None) | ||||
|     args = getattr(app.state, "args", []) | ||||
|     env = getattr(app.state, "env", {}) | ||||
| 
 | ||||
|     args = args if isinstance(args, list) else [args] | ||||
|     api_dependency = getattr(app.state, "api_dependency", None) | ||||
| 
 | ||||
|     if not command: | ||||
|     if (server_type == "stdio" and not command) or ( | ||||
|         server_type == "sse" and not args[0] | ||||
|     ): | ||||
|         # Main app lifespan (when config_path is provided) | ||||
|         async with AsyncExitStack() as stack: | ||||
|             for route in app.routes: | ||||
|                 if isinstance(route, Mount) and isinstance(route.app, FastAPI): | ||||
| @ -79,19 +84,25 @@ async def lifespan(app: FastAPI): | ||||
|                         route.app.router.lifespan_context(route.app),  # noqa | ||||
|                     ) | ||||
|             yield | ||||
| 
 | ||||
|     else: | ||||
|         server_params = StdioServerParameters( | ||||
|             command=command, | ||||
|             args=args, | ||||
|             env={**env}, | ||||
|         ) | ||||
|         if server_type == "stdio": | ||||
|             server_params = StdioServerParameters( | ||||
|                 command=command, | ||||
|                 args=args, | ||||
|                 env={**env}, | ||||
|             ) | ||||
| 
 | ||||
|         async with stdio_client(server_params) as (reader, writer): | ||||
|             async with ClientSession(reader, writer) as session: | ||||
|                 app.state.session = session | ||||
|                 await create_dynamic_endpoints(app, api_dependency=api_dependency) | ||||
|                 yield | ||||
|             async with stdio_client(server_params) as (reader, writer): | ||||
|                 async with ClientSession(reader, writer) as session: | ||||
|                     app.state.session = session | ||||
|                     await create_dynamic_endpoints(app, api_dependency=api_dependency) | ||||
|                     yield | ||||
|         if server_type == "sse": | ||||
|             async with sse_client(url=args[0]) as (reader, writer): | ||||
|                 async with ClientSession(reader, writer) as session: | ||||
|                     app.state.session = session | ||||
|                     await create_dynamic_endpoints(app, api_dependency=api_dependency) | ||||
|                     yield | ||||
| 
 | ||||
| 
 | ||||
| async def run( | ||||
| @ -104,10 +115,14 @@ async def run( | ||||
|     # Server API Key | ||||
|     api_dependency = get_verify_api_key(api_key) if api_key else None | ||||
| 
 | ||||
|     # MCP Config | ||||
|     config_path = kwargs.get("config") | ||||
|     # MCP Server | ||||
|     server_type = kwargs.get("server_type")  # "stdio" or "sse" or "http" | ||||
|     server_command = kwargs.get("server_command") | ||||
| 
 | ||||
|     # MCP Config | ||||
|     config_path = kwargs.get("config_path") | ||||
| 
 | ||||
|     # mcpo server | ||||
|     name = kwargs.get("name") or "MCP OpenAPI Proxy" | ||||
|     description = ( | ||||
|         kwargs.get("description") or "Automatically generated API from MCP Tool Schemas" | ||||
| @ -135,12 +150,18 @@ async def run( | ||||
|         allow_headers=["*"], | ||||
|     ) | ||||
| 
 | ||||
|     if server_command: | ||||
|     if server_type == "sse": | ||||
|         main_app.state.server_type = "sse" | ||||
|         main_app.state.args = server_command[0] | ||||
|         main_app.state.api_dependency = api_dependency | ||||
| 
 | ||||
|     elif server_command: | ||||
|         main_app.state.command = server_command[0] | ||||
|         main_app.state.args = server_command[1:] | ||||
|         main_app.state.env = os.environ.copy() | ||||
| 
 | ||||
|         main_app.state.api_dependency = api_dependency | ||||
| 
 | ||||
|     elif config_path: | ||||
|         with open(config_path, "r") as f: | ||||
|             config_data = json.load(f) | ||||
| @ -166,9 +187,15 @@ async def run( | ||||
|                 allow_headers=["*"], | ||||
|             ) | ||||
| 
 | ||||
|             sub_app.state.command = server_cfg["command"] | ||||
|             sub_app.state.args = server_cfg.get("args", []) | ||||
|             sub_app.state.env = {**os.environ, **server_cfg.get("env", {})} | ||||
|             if server_cfg.get("command"): | ||||
|                 # stdio | ||||
|                 sub_app.state.command = server_cfg["command"] | ||||
|                 sub_app.state.args = server_cfg.get("args", []) | ||||
|                 sub_app.state.env = {**os.environ, **server_cfg.get("env", {})} | ||||
|             if server_cfg.get("url"): | ||||
|                 # SSE | ||||
|                 sub_app.state.server_type = "sse" | ||||
|                 sub_app.state.args = server_cfg["url"] | ||||
| 
 | ||||
|             sub_app.state.api_dependency = api_dependency | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,7 @@ import pytest | ||||
| from pydantic import BaseModel, Field | ||||
| from typing import Any, List, Dict | ||||
| 
 | ||||
| from src.mcpo.utils.main import _process_schema_property | ||||
| from mcpo.utils.main import _process_schema_property | ||||
| 
 | ||||
| 
 | ||||
| _model_cache = {} | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user