mirror of
https://github.com/open-webui/mcpo
synced 2025-06-26 18:26:58 +00:00
refac
This commit is contained in:
@@ -16,7 +16,7 @@ and this project adheres to Semantic Versioning.
|
||||
|
||||
### Added
|
||||
|
||||
- 🌐 **Streamable HTTP Transport Support**: mcpo now supports MCP servers using the Streamable HTTP transport. This allows for more flexible and robust communication, including session management and resumable streams. Configure via CLI with `--server-type "streamablehttp" -- <URL>` or in the config file with `type: "streamablehttp"` and a `url`.
|
||||
- 🌐 **Streamable HTTP Transport Support**: mcpo now supports MCP servers using the Streamable HTTP transport. This allows for more flexible and robust communication, including session management and resumable streams. Configure via CLI with `--server-type "streamable_http" -- <URL>` or in the config file with `type: "streamable_http"` and a `url`.
|
||||
|
||||
## [0.0.13] - 2025-05-01
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ mcpo --port 8000 --api-key "top-secret" --server-type "sse" -- http://127.0.0.1:
|
||||
To use a Streamable HTTP-compatible MCP server, specify the server type and endpoint:
|
||||
|
||||
```bash
|
||||
mcpo --port 8000 --api-key "top-secret" --server-type "streamablehttp" -- http://127.0.0.1:8002/mcp
|
||||
mcpo --port 8000 --api-key "top-secret" --server-type "streamable_http" -- http://127.0.0.1:8002/mcp
|
||||
```
|
||||
|
||||
You can also run mcpo via Docker with no installation:
|
||||
@@ -96,7 +96,7 @@ Example config.json:
|
||||
"url": "http://127.0.0.1:8001/sse"
|
||||
},
|
||||
"mcp_streamable_http": {
|
||||
"type": "streamablehttp",
|
||||
"type": "streamable_http",
|
||||
"url": "http://127.0.0.1:8002/mcp"
|
||||
} // Streamable HTTP MCP Server
|
||||
}
|
||||
|
||||
@@ -120,12 +120,12 @@ async def lifespan(app: FastAPI):
|
||||
app.state.session = session
|
||||
await create_dynamic_endpoints(app, api_dependency=api_dependency)
|
||||
yield
|
||||
if server_type == "streamablehttp":
|
||||
if server_type == "streamablehttp" or server_type == "streamable_http":
|
||||
# Ensure URL has trailing slash to avoid redirects
|
||||
url = args[0]
|
||||
if not url.endswith('/'):
|
||||
if not url.endswith("/"):
|
||||
url = f"{url}/"
|
||||
|
||||
|
||||
# Connect using streamablehttp_client from the SDK, similar to sse_client
|
||||
async with streamablehttp_client(url=url) as (
|
||||
reader,
|
||||
@@ -150,7 +150,9 @@ async def run(
|
||||
strict_auth = kwargs.get("strict_auth", False)
|
||||
|
||||
# MCP Server
|
||||
server_type = kwargs.get("server_type") # "stdio", "sse", or "streamablehttp"
|
||||
server_type = kwargs.get(
|
||||
"server_type"
|
||||
) # "stdio", "sse", or "streamablehttp" ("streamable_http" is also accepted)
|
||||
server_command = kwargs.get("server_command")
|
||||
|
||||
# MCP Config
|
||||
@@ -168,7 +170,9 @@ async def run(
|
||||
path_prefix = kwargs.get("path_prefix") or "/"
|
||||
|
||||
# Configure basic logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger.info("Starting MCPO Server...")
|
||||
logger.info(f" Name: {name}")
|
||||
logger.info(f" Version: {version}")
|
||||
@@ -205,18 +209,24 @@ async def run(
|
||||
main_app.add_middleware(APIKeyMiddleware, api_key=api_key)
|
||||
|
||||
if server_type == "sse":
|
||||
logger.info(f"Configuring for a single SSE MCP Server with URL {server_command[0]}")
|
||||
logger.info(
|
||||
f"Configuring for a single SSE MCP Server with URL {server_command[0]}"
|
||||
)
|
||||
main_app.state.server_type = "sse"
|
||||
main_app.state.args = server_command[0] # Expects URL as the first element
|
||||
main_app.state.args = server_command[0] # Expects URL as the first element
|
||||
main_app.state.api_dependency = api_dependency
|
||||
elif server_type == "streamablehttp":
|
||||
logger.info(f"Configuring for a single StreamableHTTP MCP Server with URL {server_command[0]}")
|
||||
elif server_type == "streamablehttp" or server_type == "streamable_http":
|
||||
logger.info(
|
||||
f"Configuring for a single StreamableHTTP MCP Server with URL {server_command[0]}"
|
||||
)
|
||||
main_app.state.server_type = "streamablehttp"
|
||||
main_app.state.args = server_command[0] # Expects URL as the first element
|
||||
main_app.state.args = server_command[0] # Expects URL as the first element
|
||||
main_app.state.api_dependency = api_dependency
|
||||
elif server_command: # This handles stdio
|
||||
logger.info(f"Configuring for a single Stdio MCP Server with command: {' '.join(server_command)}")
|
||||
main_app.state.server_type = "stdio" # Explicitly set type
|
||||
elif server_command: # This handles stdio
|
||||
logger.info(
|
||||
f"Configuring for a single Stdio MCP Server with command: {' '.join(server_command)}"
|
||||
)
|
||||
main_app.state.server_type = "stdio" # Explicitly set type
|
||||
main_app.state.command = server_command[0]
|
||||
main_app.state.args = server_command[1:]
|
||||
main_app.state.env = os.environ.copy()
|
||||
@@ -230,21 +240,39 @@ async def run(
|
||||
if not mcp_servers:
|
||||
logger.error(f"No 'mcpServers' found in config file: {config_path}")
|
||||
raise ValueError("No 'mcpServers' found in config file.")
|
||||
|
||||
|
||||
logger.info("Configured MCP Servers:")
|
||||
for server_name_cfg, server_cfg_details in mcp_servers.items():
|
||||
if server_cfg_details.get("command"):
|
||||
args_info = f" with args: {server_cfg_details['args']}" if server_cfg_details.get("args") else ""
|
||||
logger.info(f" Configuring Stdio MCP Server '{server_name_cfg}' with command: {server_cfg_details['command']}{args_info}")
|
||||
elif server_cfg_details.get("type") == "sse" and server_cfg_details.get("url"):
|
||||
logger.info(f" Configuring SSE MCP Server '{server_name_cfg}' with URL: {server_cfg_details['url']}")
|
||||
elif server_cfg_details.get("type") == "streamablehttp" and server_cfg_details.get("url"):
|
||||
logger.info(f" Configuring StreamableHTTP MCP Server '{server_name_cfg}' with URL: {server_cfg_details['url']}")
|
||||
elif server_cfg_details.get("url"): # Fallback for old SSE config
|
||||
logger.info(f" Configuring SSE (fallback) MCP Server '{server_name_cfg}' with URL: {server_cfg_details['url']}")
|
||||
args_info = (
|
||||
f" with args: {server_cfg_details['args']}"
|
||||
if server_cfg_details.get("args")
|
||||
else ""
|
||||
)
|
||||
logger.info(
|
||||
f" Configuring Stdio MCP Server '{server_name_cfg}' with command: {server_cfg_details['command']}{args_info}"
|
||||
)
|
||||
elif server_cfg_details.get("type") == "sse" and server_cfg_details.get(
|
||||
"url"
|
||||
):
|
||||
logger.info(
|
||||
f" Configuring SSE MCP Server '{server_name_cfg}' with URL: {server_cfg_details['url']}"
|
||||
)
|
||||
elif (
|
||||
server_cfg_details.get("type") == "streamablehttp"
|
||||
or server_cfg_details.get("type") == "streamable_http"
|
||||
) and server_cfg_details.get("url"):
|
||||
logger.info(
|
||||
f" Configuring StreamableHTTP MCP Server '{server_name_cfg}' with URL: {server_cfg_details['url']}"
|
||||
)
|
||||
elif server_cfg_details.get("url"): # Fallback for old SSE config
|
||||
logger.info(
|
||||
f" Configuring SSE (fallback) MCP Server '{server_name_cfg}' with URL: {server_cfg_details['url']}"
|
||||
)
|
||||
else:
|
||||
logger.warning(f" Unknown configuration for MCP server: {server_name_cfg}")
|
||||
|
||||
logger.warning(
|
||||
f" Unknown configuration for MCP server: {server_name_cfg}"
|
||||
)
|
||||
|
||||
main_app.description += "\n\n- **available tools**:"
|
||||
for server_name, server_cfg in mcp_servers.items():
|
||||
@@ -274,18 +302,22 @@ async def run(
|
||||
if server_config_type == "sse" and server_cfg.get("url"):
|
||||
sub_app.state.server_type = "sse"
|
||||
sub_app.state.args = server_cfg["url"]
|
||||
elif server_config_type == "streamablehttp" and server_cfg.get("url"):
|
||||
elif (
|
||||
server_config_type == "streamablehttp"
|
||||
or server_config_type == "streamable_http"
|
||||
) and server_cfg.get("url"):
|
||||
# Store the URL with trailing slash to avoid redirects
|
||||
url = server_cfg["url"]
|
||||
if not url.endswith('/'):
|
||||
if not url.endswith("/"):
|
||||
url = f"{url}/"
|
||||
sub_app.state.server_type = "streamablehttp"
|
||||
sub_app.state.args = url
|
||||
elif not server_config_type and server_cfg.get("url"): # Fallback for old SSE config
|
||||
elif not server_config_type and server_cfg.get(
|
||||
"url"
|
||||
): # Fallback for old SSE config
|
||||
sub_app.state.server_type = "sse"
|
||||
sub_app.state.args = server_cfg["url"]
|
||||
|
||||
|
||||
# Add middleware to protect also documentation and spec
|
||||
if api_key and strict_auth:
|
||||
sub_app.add_middleware(APIKeyMiddleware, api_key=api_key)
|
||||
|
||||
Reference in New Issue
Block a user