mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
refactor(MCP): Replace MCPRouter with FastMCP Proxy (#8877)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
0221f21c12
commit
d6d5499416
1
.github/dependabot.yml
vendored
1
.github/dependabot.yml
vendored
@ -16,7 +16,6 @@ updates:
|
||||
mcp-packages:
|
||||
patterns:
|
||||
- "mcp"
|
||||
- "mcpm"
|
||||
security-all:
|
||||
applies-to: "security-updates"
|
||||
patterns:
|
||||
|
||||
@ -9,7 +9,6 @@ import argparse
|
||||
import asyncio
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
import shutil
|
||||
@ -26,8 +25,6 @@ from fastapi import Depends, FastAPI, HTTPException, Request, UploadFile
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from fastapi.security import APIKeyHeader
|
||||
from mcpm import MCPRouter, RouterConfig
|
||||
from mcpm.router.router import logger as mcp_router_logger
|
||||
from openhands_aci.editor.editor import OHEditor
|
||||
from openhands_aci.editor.exceptions import ToolError
|
||||
from openhands_aci.editor.results import ToolResult
|
||||
@ -37,6 +34,7 @@ from starlette.background import BackgroundTask
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from uvicorn import run
|
||||
|
||||
from openhands.core.config.mcp_config import MCPStdioServerConfig
|
||||
from openhands.core.exceptions import BrowserUnavailableException
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.events.action import (
|
||||
@ -63,20 +61,18 @@ from openhands.events.serialization import event_from_dict, event_to_dict
|
||||
from openhands.runtime.browser import browse
|
||||
from openhands.runtime.browser.browser_env import BrowserEnv
|
||||
from openhands.runtime.file_viewer_server import start_file_viewer_server
|
||||
|
||||
# Import our custom MCP Proxy Manager
|
||||
from openhands.runtime.mcp.proxy import MCPProxyManager
|
||||
from openhands.runtime.plugins import ALL_PLUGINS, JupyterPlugin, Plugin, VSCodePlugin
|
||||
from openhands.runtime.utils import find_available_tcp_port
|
||||
from openhands.runtime.utils.bash import BashSession
|
||||
from openhands.runtime.utils.files import insert_lines, read_lines
|
||||
from openhands.runtime.utils.log_capture import capture_logs
|
||||
from openhands.runtime.utils.memory_monitor import MemoryMonitor
|
||||
from openhands.runtime.utils.runtime_init import init_user_and_working_directory
|
||||
from openhands.runtime.utils.system_stats import get_system_stats
|
||||
from openhands.utils.async_utils import call_sync_from_async, wait_all
|
||||
|
||||
# Set MCP router logger to the same level as the main logger
|
||||
mcp_router_logger.setLevel(logger.getEffectiveLevel())
|
||||
|
||||
|
||||
if sys.platform == 'win32':
|
||||
from openhands.runtime.utils.windows_bash import WindowsPowershellSession
|
||||
|
||||
@ -471,7 +467,7 @@ class ActionExecutor:
|
||||
filepath = self._resolve_path(action.path, working_dir)
|
||||
try:
|
||||
if filepath.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
|
||||
with open(filepath, 'rb') as file: # noqa: ASYNC101
|
||||
with open(filepath, 'rb') as file:
|
||||
image_data = file.read()
|
||||
encoded_image = base64.b64encode(image_data).decode('utf-8')
|
||||
mime_type, _ = mimetypes.guess_type(filepath)
|
||||
@ -481,13 +477,13 @@ class ActionExecutor:
|
||||
|
||||
return FileReadObservation(path=filepath, content=encoded_image)
|
||||
elif filepath.lower().endswith('.pdf'):
|
||||
with open(filepath, 'rb') as file: # noqa: ASYNC101
|
||||
with open(filepath, 'rb') as file:
|
||||
pdf_data = file.read()
|
||||
encoded_pdf = base64.b64encode(pdf_data).decode('utf-8')
|
||||
encoded_pdf = f'data:application/pdf;base64,{encoded_pdf}'
|
||||
return FileReadObservation(path=filepath, content=encoded_pdf)
|
||||
elif filepath.lower().endswith(('.mp4', '.webm', '.ogg')):
|
||||
with open(filepath, 'rb') as file: # noqa: ASYNC101
|
||||
with open(filepath, 'rb') as file:
|
||||
video_data = file.read()
|
||||
encoded_video = base64.b64encode(video_data).decode('utf-8')
|
||||
mime_type, _ = mimetypes.guess_type(filepath)
|
||||
@ -497,7 +493,7 @@ class ActionExecutor:
|
||||
|
||||
return FileReadObservation(path=filepath, content=encoded_video)
|
||||
|
||||
with open(filepath, 'r', encoding='utf-8') as file: # noqa: ASYNC101
|
||||
with open(filepath, 'r', encoding='utf-8') as file:
|
||||
lines = read_lines(file.readlines(), action.start, action.end)
|
||||
except FileNotFoundError:
|
||||
return ErrorObservation(
|
||||
@ -530,7 +526,7 @@ class ActionExecutor:
|
||||
|
||||
mode = 'w' if not file_exists else 'r+'
|
||||
try:
|
||||
with open(filepath, mode, encoding='utf-8') as file: # noqa: ASYNC101
|
||||
with open(filepath, mode, encoding='utf-8') as file:
|
||||
if mode != 'w':
|
||||
all_lines = file.readlines()
|
||||
new_file = insert_lines(insert, all_lines, action.start, action.end)
|
||||
@ -654,14 +650,11 @@ if __name__ == '__main__':
|
||||
plugins_to_load.append(ALL_PLUGINS[plugin]()) # type: ignore
|
||||
|
||||
client: ActionExecutor | None = None
|
||||
mcp_router: MCPRouter | None = None
|
||||
MCP_ROUTER_PROFILE_PATH = os.path.join(
|
||||
os.path.dirname(__file__), 'mcp', 'config.json'
|
||||
)
|
||||
mcp_proxy_manager: MCPProxyManager | None = None
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
global client, mcp_router
|
||||
global client, mcp_proxy_manager
|
||||
logger.info('Initializing ActionExecutor...')
|
||||
client = ActionExecutor(
|
||||
plugins_to_load,
|
||||
@ -676,63 +669,36 @@ if __name__ == '__main__':
|
||||
# Check if we're on Windows
|
||||
is_windows = sys.platform == 'win32'
|
||||
|
||||
# Initialize and mount MCP Router (skip on Windows)
|
||||
# Initialize and mount MCP Proxy Manager (skip on Windows)
|
||||
if is_windows:
|
||||
logger.info('Skipping MCP Router initialization on Windows')
|
||||
mcp_router = None
|
||||
logger.info('Skipping MCP Proxy initialization on Windows')
|
||||
mcp_proxy_manager = None
|
||||
else:
|
||||
logger.info('Initializing MCP Router...')
|
||||
mcp_router = MCPRouter(
|
||||
profile_path=MCP_ROUTER_PROFILE_PATH,
|
||||
router_config=RouterConfig(
|
||||
api_key=SESSION_API_KEY,
|
||||
auth_enabled=bool(SESSION_API_KEY),
|
||||
),
|
||||
logger.info('Initializing MCP Proxy Manager...')
|
||||
# Create a MCP Proxy Manager
|
||||
mcp_proxy_manager = MCPProxyManager(
|
||||
auth_enabled=bool(SESSION_API_KEY),
|
||||
api_key=SESSION_API_KEY,
|
||||
logger_level=logger.getEffectiveLevel(),
|
||||
)
|
||||
mcp_proxy_manager.initialize()
|
||||
# Mount the proxy to the app
|
||||
allowed_origins = ['*']
|
||||
sse_app = await mcp_router.get_sse_server_app(
|
||||
allow_origins=allowed_origins, include_lifespan=False
|
||||
)
|
||||
|
||||
# Only mount SSE app if MCP Router is initialized (not on Windows)
|
||||
if mcp_router is not None:
|
||||
# Check for route conflicts before mounting
|
||||
main_app_routes = {route.path for route in app.routes}
|
||||
sse_app_routes = {route.path for route in sse_app.routes}
|
||||
conflicting_routes = main_app_routes.intersection(sse_app_routes)
|
||||
|
||||
if conflicting_routes:
|
||||
logger.error(f'Route conflicts detected: {conflicting_routes}')
|
||||
raise RuntimeError(
|
||||
f'Cannot mount SSE app - conflicting routes found: {conflicting_routes}'
|
||||
)
|
||||
|
||||
app.mount('/', sse_app)
|
||||
logger.info(
|
||||
f'Mounted MCP Router SSE app at root path with allowed origins: {allowed_origins}'
|
||||
)
|
||||
|
||||
# Additional debug logging
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug('Main app routes:')
|
||||
for route in main_app_routes:
|
||||
logger.debug(f' {route}')
|
||||
logger.debug('MCP SSE server app routes:')
|
||||
for route in sse_app_routes:
|
||||
logger.debug(f' {route}')
|
||||
try:
|
||||
await mcp_proxy_manager.mount_to_app(app, allowed_origins)
|
||||
except Exception as e:
|
||||
logger.error(f'Error mounting MCP Proxy: {e}', exc_info=True)
|
||||
raise RuntimeError(f'Cannot mount MCP Proxy: {e}')
|
||||
|
||||
yield
|
||||
|
||||
# Clean up & release the resources
|
||||
logger.info('Shutting down MCP Router...')
|
||||
if mcp_router:
|
||||
try:
|
||||
await mcp_router.shutdown()
|
||||
logger.info('MCP Router shutdown successfully.')
|
||||
except Exception as e:
|
||||
logger.error(f'Error shutting down MCP Router: {e}', exc_info=True)
|
||||
logger.info('Shutting down MCP Proxy Manager...')
|
||||
if mcp_proxy_manager:
|
||||
del mcp_proxy_manager
|
||||
mcp_proxy_manager = None
|
||||
else:
|
||||
logger.info('MCP Router instance not found for shutdown.')
|
||||
logger.info('MCP Proxy Manager instance not found for shutdown.')
|
||||
|
||||
logger.info('Closing ActionExecutor...')
|
||||
if client:
|
||||
@ -824,6 +790,9 @@ if __name__ == '__main__':
|
||||
# Check if we're on Windows
|
||||
is_windows = sys.platform == 'win32'
|
||||
|
||||
# Access the global mcp_proxy_manager variable
|
||||
global mcp_proxy_manager
|
||||
|
||||
if is_windows:
|
||||
# On Windows, just return a success response without doing anything
|
||||
logger.info(
|
||||
@ -838,17 +807,10 @@ if __name__ == '__main__':
|
||||
)
|
||||
|
||||
# Non-Windows implementation
|
||||
assert mcp_router is not None
|
||||
assert os.path.exists(MCP_ROUTER_PROFILE_PATH)
|
||||
|
||||
# Use synchronous file operations outside of async function
|
||||
def read_profile():
|
||||
with open(MCP_ROUTER_PROFILE_PATH, 'r') as f:
|
||||
return json.load(f)
|
||||
|
||||
current_profile = read_profile()
|
||||
assert 'default' in current_profile
|
||||
assert isinstance(current_profile['default'], list)
|
||||
if mcp_proxy_manager is None:
|
||||
raise HTTPException(
|
||||
status_code=500, detail='MCP Proxy Manager is not initialized'
|
||||
)
|
||||
|
||||
# Get the request body
|
||||
mcp_tools_to_sync = await request.json()
|
||||
@ -856,31 +818,17 @@ if __name__ == '__main__':
|
||||
raise HTTPException(
|
||||
status_code=400, detail='Request must be a list of MCP tools to sync'
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f'Updating MCP server to: {json.dumps(mcp_tools_to_sync, indent=2)}.\nPrevious profile: {json.dumps(current_profile, indent=2)}'
|
||||
f'Updating MCP server with tools: {json.dumps(mcp_tools_to_sync, indent=2)}'
|
||||
)
|
||||
current_profile['default'] = mcp_tools_to_sync
|
||||
|
||||
# Use synchronous file operations outside of async function
|
||||
def write_profile(profile):
|
||||
with open(MCP_ROUTER_PROFILE_PATH, 'w') as f:
|
||||
json.dump(profile, f)
|
||||
|
||||
write_profile(current_profile)
|
||||
|
||||
# Manually reload the profile and update the servers
|
||||
mcp_router.profile_manager.reload()
|
||||
servers_wait_for_update = mcp_router.get_unique_servers()
|
||||
async with capture_logs('mcpm.router.router') as log_capture:
|
||||
await mcp_router.update_servers(servers_wait_for_update)
|
||||
router_error_log = log_capture.getvalue()
|
||||
|
||||
logger.info(
|
||||
f'MCP router updated successfully with unique servers: {servers_wait_for_update}'
|
||||
)
|
||||
if router_error_log:
|
||||
logger.warning(f'Some MCP servers failed to be added: {router_error_log}')
|
||||
mcp_tools_to_sync = [MCPStdioServerConfig(**tool) for tool in mcp_tools_to_sync]
|
||||
try:
|
||||
await mcp_proxy_manager.update_and_remount(app, mcp_tools_to_sync, ['*'])
|
||||
logger.info('MCP Proxy Manager updated and remounted successfully')
|
||||
router_error_log = ''
|
||||
except Exception as e:
|
||||
logger.error(f'Error updating MCP Proxy Manager: {e}', exc_info=True)
|
||||
router_error_log = str(e)
|
||||
|
||||
return JSONResponse(
|
||||
status_code=200,
|
||||
@ -915,7 +863,7 @@ if __name__ == '__main__':
|
||||
)
|
||||
|
||||
zip_path = os.path.join(full_dest_path, file.filename)
|
||||
with open(zip_path, 'wb') as buffer: # noqa: ASYNC101
|
||||
with open(zip_path, 'wb') as buffer:
|
||||
shutil.copyfileobj(file.file, buffer)
|
||||
|
||||
# Extract the zip file
|
||||
@ -928,7 +876,7 @@ if __name__ == '__main__':
|
||||
else:
|
||||
# For single file uploads
|
||||
file_path = os.path.join(full_dest_path, file.filename)
|
||||
with open(file_path, 'wb') as buffer: # noqa: ASYNC101
|
||||
with open(file_path, 'wb') as buffer:
|
||||
shutil.copyfileobj(file.file, buffer)
|
||||
logger.debug(f'Uploaded file {file.filename} to {destination}')
|
||||
|
||||
|
||||
@ -435,7 +435,7 @@ class ActionExecutionClient(Runtime):
|
||||
# We should always include the runtime as an MCP server whenever there's > 0 stdio servers
|
||||
updated_mcp_config.sse_servers.append(
|
||||
MCPSSEServerConfig(
|
||||
url=self.action_execution_server_url.rstrip('/') + '/sse',
|
||||
url=self.action_execution_server_url.rstrip('/') + '/mcp/sse',
|
||||
api_key=self.session_api_key,
|
||||
)
|
||||
)
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
{
|
||||
"default": []
|
||||
"mcpServers": {
|
||||
"default": {}
|
||||
},
|
||||
"tools": []
|
||||
}
|
||||
|
||||
71
openhands/runtime/mcp/proxy/README.md
Normal file
71
openhands/runtime/mcp/proxy/README.md
Normal file
@ -0,0 +1,71 @@
|
||||
# MCP Proxy Manager
|
||||
|
||||
This module provides a manager class for handling FastMCP proxy instances in OpenHands, including initialization, configuration, and mounting to FastAPI applications.
|
||||
|
||||
## Overview
|
||||
|
||||
The `MCPProxyManager` class encapsulates all the functionality related to creating, configuring, and managing FastMCP proxy instances. It simplifies the process of:
|
||||
|
||||
1. Initializing a FastMCP proxy
|
||||
2. Configuring the proxy with tools
|
||||
3. Mounting the proxy to a FastAPI application
|
||||
4. Updating the proxy configuration
|
||||
5. Shutting down the proxy
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```python
|
||||
from openhands.runtime.mcp.proxy import MCPProxyManager
|
||||
from fastapi import FastAPI
|
||||
|
||||
# Create a FastAPI app
|
||||
app = FastAPI()
|
||||
|
||||
# Create a proxy manager
|
||||
proxy_manager = MCPProxyManager(
|
||||
name="MyProxyServer",
|
||||
auth_enabled=True,
|
||||
api_key="my-api-key"
|
||||
)
|
||||
|
||||
# Initialize the proxy
|
||||
proxy_manager.initialize()
|
||||
|
||||
# Mount the proxy to the app
|
||||
await proxy_manager.mount_to_app(app, allow_origins=["*"])
|
||||
|
||||
# Update the tools configuration
|
||||
tools = [
|
||||
{
|
||||
"name": "my_tool",
|
||||
"description": "My tool description",
|
||||
"parameters": {...}
|
||||
}
|
||||
]
|
||||
proxy_manager.update_tools(tools)
|
||||
|
||||
# Update and remount the proxy
|
||||
await proxy_manager.update_and_remount(app, tools, allow_origins=["*"])
|
||||
|
||||
# Shutdown the proxy
|
||||
await proxy_manager.shutdown()
|
||||
```
|
||||
|
||||
### In-Memory Configuration
|
||||
|
||||
The `MCPProxyManager` maintains the configuration in-memory, eliminating the need for file-based configuration. This makes it easier to update the configuration and reduces the complexity of the code.
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Simplified API**: The `MCPProxyManager` provides a simple and intuitive API for managing FastMCP proxies.
|
||||
2. **In-Memory Configuration**: Configuration is maintained in-memory, eliminating the need for file I/O operations.
|
||||
3. **Improved Error Handling**: The manager provides better error handling and logging for proxy operations.
|
||||
4. **Cleaner Code**: By encapsulating proxy-related functionality in a dedicated class, the code is more maintainable and easier to understand.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
The `MCPProxyManager` uses the `FastMCP.as_proxy()` method to create a proxy server. It manages the lifecycle of the proxy, including initialization, configuration updates, and shutdown.
|
||||
|
||||
When updating the tools configuration, the manager creates a new proxy with the updated configuration and remounts it to the FastAPI application, ensuring that the proxy is always up-to-date with the latest configuration.
|
||||
7
openhands/runtime/mcp/proxy/__init__.py
Normal file
7
openhands/runtime/mcp/proxy/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
"""
|
||||
MCP Proxy module for OpenHands.
|
||||
"""
|
||||
|
||||
from openhands.runtime.mcp.proxy.manager import MCPProxyManager
|
||||
|
||||
__all__ = ['MCPProxyManager']
|
||||
138
openhands/runtime/mcp/proxy/manager.py
Normal file
138
openhands/runtime/mcp/proxy/manager.py
Normal file
@ -0,0 +1,138 @@
|
||||
"""
|
||||
MCP Proxy Manager for OpenHands.
|
||||
|
||||
This module provides a manager class for handling FastMCP proxy instances,
|
||||
including initialization, configuration, and mounting to FastAPI applications.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.utilities.logging import get_logger as fastmcp_get_logger
|
||||
|
||||
from openhands.core.config.mcp_config import MCPStdioServerConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
fastmcp_logger = fastmcp_get_logger('fastmcp')
|
||||
|
||||
|
||||
class MCPProxyManager:
|
||||
"""
|
||||
Manager for FastMCP proxy instances.
|
||||
|
||||
This class encapsulates all the functionality related to creating, configuring,
|
||||
and managing FastMCP proxy instances, including mounting them to FastAPI applications.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
auth_enabled: bool = False,
|
||||
api_key: Optional[str] = None,
|
||||
logger_level: Optional[int] = None,
|
||||
):
|
||||
"""
|
||||
Initialize the MCP Proxy Manager.
|
||||
|
||||
Args:
|
||||
name: Name of the proxy server
|
||||
auth_enabled: Whether authentication is enabled
|
||||
api_key: API key for authentication (required if auth_enabled is True)
|
||||
logger_level: Logging level for the FastMCP logger
|
||||
"""
|
||||
self.auth_enabled = auth_enabled
|
||||
self.api_key = api_key
|
||||
self.proxy: Optional[FastMCP] = None
|
||||
# Initialize with a valid configuration format for FastMCP
|
||||
self.config: dict[str, Any] = {
|
||||
'mcpServers': {},
|
||||
}
|
||||
|
||||
# Configure FastMCP logger
|
||||
if logger_level is not None:
|
||||
fastmcp_logger.setLevel(logger_level)
|
||||
|
||||
def initialize(self) -> None:
|
||||
"""
|
||||
Initialize the FastMCP proxy with the current configuration.
|
||||
"""
|
||||
if len(self.config['mcpServers']) == 0:
|
||||
logger.info(
|
||||
f"No MCP servers configured for FastMCP Proxy, skipping initialization."
|
||||
)
|
||||
return None
|
||||
|
||||
# Create a new proxy with the current configuration
|
||||
self.proxy = FastMCP.as_proxy(
|
||||
self.config,
|
||||
auth_enabled=self.auth_enabled,
|
||||
api_key=self.api_key,
|
||||
)
|
||||
|
||||
logger.info(f"FastMCP Proxy initialized successfully")
|
||||
|
||||
async def mount_to_app(
|
||||
self, app: FastAPI, allow_origins: Optional[list[str]] = None
|
||||
) -> None:
|
||||
"""
|
||||
Mount the SSE server app to a FastAPI application.
|
||||
|
||||
Args:
|
||||
app: FastAPI application to mount to
|
||||
allow_origins: List of allowed origins for CORS
|
||||
"""
|
||||
if len(self.config['mcpServers']) == 0:
|
||||
logger.info(
|
||||
f"No MCP servers configured for FastMCP Proxy, skipping mount."
|
||||
)
|
||||
return
|
||||
|
||||
if not self.proxy:
|
||||
raise ValueError('FastMCP Proxy is not initialized')
|
||||
|
||||
# Get the SSE app
|
||||
# mcp_app = self.proxy.http_app(path='/shttp')
|
||||
mcp_app = self.proxy.http_app(path='/sse', transport='sse')
|
||||
app.mount('/mcp', mcp_app)
|
||||
|
||||
# Remove any existing mounts at root path
|
||||
if '/mcp' in app.routes:
|
||||
app.routes.remove('/mcp')
|
||||
|
||||
app.mount('/', mcp_app)
|
||||
logger.info(f"Mounted FastMCP Proxy app at /mcp")
|
||||
|
||||
|
||||
async def update_and_remount(
|
||||
self,
|
||||
app: FastAPI,
|
||||
stdio_servers: list[MCPStdioServerConfig],
|
||||
allow_origins: Optional[list[str]] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Update the tools configuration and remount the proxy to the app.
|
||||
|
||||
This is a convenience method that combines updating the tools,
|
||||
shutting down the existing proxy, initializing a new one, and
|
||||
mounting it to the app.
|
||||
|
||||
Args:
|
||||
app: FastAPI application to mount to
|
||||
tools: List of tool configurations
|
||||
allow_origins: List of allowed origins for CORS
|
||||
"""
|
||||
tools = {
|
||||
t.name: t.model_dump()
|
||||
for t in stdio_servers
|
||||
}
|
||||
self.config['mcpServers'] = tools
|
||||
|
||||
del self.proxy
|
||||
self.proxy = None
|
||||
|
||||
# Initialize a new proxy
|
||||
self.initialize()
|
||||
|
||||
# Mount the new proxy to the app
|
||||
await self.mount_to_app(app, allow_origins)
|
||||
156
poetry.lock
generated
156
poetry.lock
generated
@ -2116,52 +2116,6 @@ files = [
|
||||
{file = "docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "duckdb"
|
||||
version = "1.3.0"
|
||||
description = "DuckDB in-process database"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "duckdb-1.3.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:fc65c1e97aa010359c43c0342ea423e6efa3cb8c8e3f133b0765451ce674e3db"},
|
||||
{file = "duckdb-1.3.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:8fc91b629646679e33806342510335ccbbeaf2b823186f0ae829fd48e7a63c66"},
|
||||
{file = "duckdb-1.3.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:1a69b970553fd015c557238d427ef00be3c8ed58c3bc3641aef987e33f8bf614"},
|
||||
{file = "duckdb-1.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1003e84c07b84680cee6d06e4795b6e861892474704f7972058594a52c7473cf"},
|
||||
{file = "duckdb-1.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:992239b54ca6f015ad0ed0d80f3492c065313c4641df0a226183b8860cb7f5b0"},
|
||||
{file = "duckdb-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0ba1c5af59e8147216149b814b1970b8f7e3c240494a9688171390db3c504b29"},
|
||||
{file = "duckdb-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:57b794ca28e22b23bd170506cb1d4704a3608e67f0fe33273db9777b69bdf26a"},
|
||||
{file = "duckdb-1.3.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:60a58b85929754abb21db1e739d2f53eaef63e6015e62ba58eae3425030e7935"},
|
||||
{file = "duckdb-1.3.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:1d46b5a20f078b1b2284243e02a1fde7e12cbb8d205fce62e4700bcfe6a09881"},
|
||||
{file = "duckdb-1.3.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:0044e5ffb2d46308099640a92f99980a44e12bb68642aa9e6b08acbf300d64a1"},
|
||||
{file = "duckdb-1.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cb813de2ca2f5e7c77392a67bdcaa174bfd69ebbfdfc983024af270c77a0447"},
|
||||
{file = "duckdb-1.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a0c993eb6df2b30b189ad747f3aea1b0b87b78ab7f80c6e7c57117b6e8dbfb0"},
|
||||
{file = "duckdb-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6728e209570d36ece66dd7249e5d6055326321137cd807f26300733283930cd4"},
|
||||
{file = "duckdb-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:7e652b7c8dbdb91a94fd7d543d3e115d24a25aa0791a373a852e20cb7bb21154"},
|
||||
{file = "duckdb-1.3.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f24038fe9b83dcbaeafb1ed76ec3b3f38943c1c8d27ab464ad384db8a6658b61"},
|
||||
{file = "duckdb-1.3.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:956c85842841bef68f4a5388c6b225b933151a7c06d568390fc895fc44607913"},
|
||||
{file = "duckdb-1.3.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:efe883d822ed56fcfbb6a7b397c13f6a0d2eaeb3bc4ef4510f84fadb3dfe416d"},
|
||||
{file = "duckdb-1.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3872a3a1b80ffba5264ea236a3754d0c41d3c7b01bdf8cdcb1c180fc1b8dc8e2"},
|
||||
{file = "duckdb-1.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30bf45ad78a5a997f378863e036e917b481d18d685e5c977cd0a3faf2e31fbaf"},
|
||||
{file = "duckdb-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85cbd8e1d65df8a0780023baf5045d3033fabd154799bc9ea6d9ab5728f41eb3"},
|
||||
{file = "duckdb-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8754c40dac0f26d9fb0363bbb5df02f7a61ce6a6728d5efc02c3bc925d7c89c3"},
|
||||
{file = "duckdb-1.3.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:176b9818d940c52ac7f31c64a98cf172d7c19d2a006017c9c4e9c06c246e36bf"},
|
||||
{file = "duckdb-1.3.0-cp313-cp313-macosx_12_0_universal2.whl", hash = "sha256:03981f7e8793f07a4a9a2ba387640e71d0a99ebcaf8693ab09f96d59e628b713"},
|
||||
{file = "duckdb-1.3.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:a177d55a38a62fdf79b59a0eaa32531a1dbb443265f6d67f64992cc1e82b755c"},
|
||||
{file = "duckdb-1.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b1c30e3749823147d5578bc3f01f35d1a0433a1c768908d946056ec8d6e1757e"},
|
||||
{file = "duckdb-1.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5855f3a564baf22eeeab70c120b51f5a11914f1f1634f03382daeb6b1dea4c62"},
|
||||
{file = "duckdb-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b1fac15a48056f7c2739cf8800873063ba2f691e91a9b2fc167658a401ca76a"},
|
||||
{file = "duckdb-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:fbdfc1c0b83b90f780ae74038187ee696bb56ab727a289752372d7ec42dda65b"},
|
||||
{file = "duckdb-1.3.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:5f6b5d725546ad30abc125a6813734b493fea694bc3123e991c480744573c2f1"},
|
||||
{file = "duckdb-1.3.0-cp39-cp39-macosx_12_0_universal2.whl", hash = "sha256:fcbcc9b956b06cf5ee94629438ecab88de89b08b5620fcda93665c222ab18cd4"},
|
||||
{file = "duckdb-1.3.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:2d32f2d44105e1705d8a0fb6d6d246fd69aff82c80ad23293266244b66b69012"},
|
||||
{file = "duckdb-1.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0aa7a5c0dcb780850e6da1227fb1d552af8e1a5091e02667ab6ace61ab49ce6c"},
|
||||
{file = "duckdb-1.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cb254fd5405f3edbd7d962ba39c72e4ab90b37cb4d0e34846089796c8078419"},
|
||||
{file = "duckdb-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a7d337b58c59fd2cd9faae531b05d940f8d92bdc2e14cb6e9a5a37675ad2742d"},
|
||||
{file = "duckdb-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3cea3a345755c7dbcb58403dbab8befd499c82f0d27f893a4c1d4b8cf56ec54"},
|
||||
{file = "duckdb-1.3.0.tar.gz", hash = "sha256:09aaa4b1dca24f4d1f231e7ae66b6413e317b7e04e2753541d42df6c8113fac7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dulwich"
|
||||
version = "0.22.8"
|
||||
@ -2969,8 +2923,8 @@ files = [
|
||||
google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]}
|
||||
google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev"
|
||||
proto-plus = [
|
||||
{version = ">=1.22.3,<2.0.0dev"},
|
||||
{version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=1.22.3,<2.0.0dev"},
|
||||
]
|
||||
protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev"
|
||||
|
||||
@ -2992,8 +2946,8 @@ googleapis-common-protos = ">=1.56.2,<2.0.0"
|
||||
grpcio = {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}
|
||||
grpcio-status = {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}
|
||||
proto-plus = [
|
||||
{version = ">=1.22.3,<2.0.0"},
|
||||
{version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=1.22.3,<2.0.0"},
|
||||
]
|
||||
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0"
|
||||
requests = ">=2.18.0,<3.0.0"
|
||||
@ -3211,8 +3165,8 @@ google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0", extras
|
||||
google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0"
|
||||
grpc-google-iam-v1 = ">=0.14.0,<1.0.0"
|
||||
proto-plus = [
|
||||
{version = ">=1.22.3,<2.0.0"},
|
||||
{version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=1.22.3,<2.0.0"},
|
||||
]
|
||||
protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0"
|
||||
|
||||
@ -5398,30 +5352,6 @@ cli = ["python-dotenv (>=1.0.0)", "typer (>=0.12.4)"]
|
||||
rich = ["rich (>=13.9.4)"]
|
||||
ws = ["websockets (>=15.0.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "mcpm"
|
||||
version = "1.12.0"
|
||||
description = "MCPM - Model Context Protocol Manager"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "mcpm-1.12.0-py3-none-any.whl", hash = "sha256:ed3a87300420bcdb9cd12ef290179fda5bd51eb2f4cd3e793084d83eed91b249"},
|
||||
{file = "mcpm-1.12.0.tar.gz", hash = "sha256:e9d2b852b90d7fd62dede584f035dd6b2b3d068d233e96b82aead835f81a911a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.1.3"
|
||||
duckdb = ">=1.2.2"
|
||||
mcp = ">=1.8.0"
|
||||
prompt-toolkit = ">=3.0.0"
|
||||
psutil = ">=7.0.0"
|
||||
pydantic = ">=2.5.1"
|
||||
requests = ">=2.28.0"
|
||||
rich = ">=12.0.0"
|
||||
ruamel-yaml = ">=0.18.10"
|
||||
watchfiles = ">=1.0.4"
|
||||
|
||||
[[package]]
|
||||
name = "mdurl"
|
||||
version = "0.1.2"
|
||||
@ -6500,8 +6430,8 @@ files = [
|
||||
[package.dependencies]
|
||||
googleapis-common-protos = ">=1.52,<2.0"
|
||||
grpcio = [
|
||||
{version = ">=1.63.2,<2.0.0", markers = "python_version < \"3.13\""},
|
||||
{version = ">=1.66.2,<2.0.0", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=1.63.2,<2.0.0", markers = "python_version < \"3.13\""},
|
||||
]
|
||||
opentelemetry-api = ">=1.15,<2.0"
|
||||
opentelemetry-exporter-otlp-proto-common = "1.34.0"
|
||||
@ -8937,82 +8867,6 @@ files = [
|
||||
[package.dependencies]
|
||||
pyasn1 = ">=0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "ruamel-yaml"
|
||||
version = "0.18.12"
|
||||
description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "ruamel.yaml-0.18.12-py3-none-any.whl", hash = "sha256:790ba4c48b6a6e6b12b532a7308779eb12d2aaab3a80fdb8389216f28ea2b287"},
|
||||
{file = "ruamel.yaml-0.18.12.tar.gz", hash = "sha256:5a38fd5ce39d223bebb9e3a6779e86b9427a03fb0bf9f270060f8b149cffe5e2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["mercurial (>5.7)", "ryd"]
|
||||
jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruamel-yaml-clib"
|
||||
version = "0.2.12"
|
||||
description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
markers = "platform_python_implementation == \"CPython\""
|
||||
files = [
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"},
|
||||
{file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.11.11"
|
||||
@ -11760,4 +11614,4 @@ cffi = ["cffi (>=1.11)"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.12,<3.14"
|
||||
content-hash = "d9f6c24fa80dd191f180af0c802ea11ecf514d86aaa421cb19a9bb497362c101"
|
||||
content-hash = "8c960ca43a540bfd96dc029d45fa4e0a4a3f75c2996feecaa8b989c348655f70"
|
||||
|
||||
@ -67,7 +67,6 @@ poetry = "^2.1.2"
|
||||
anyio = "4.9.0"
|
||||
pythonnet = "*"
|
||||
fastmcp = "^2.5.2"
|
||||
mcpm = "1.12.0"
|
||||
python-frontmatter = "^1.1.0"
|
||||
# TODO: Should these go into the runtime group?
|
||||
ipywidgets = "^8.1.5"
|
||||
|
||||
@ -114,9 +114,11 @@ def test_default_activated_tools():
|
||||
)
|
||||
with open(mcp_config_path, 'r') as f:
|
||||
mcp_config = json.load(f)
|
||||
assert 'default' in mcp_config
|
||||
assert 'mcpServers' in mcp_config
|
||||
assert 'default' in mcp_config['mcpServers']
|
||||
assert 'tools' in mcp_config
|
||||
# no tools are always activated yet
|
||||
assert len(mcp_config['default']) == 0
|
||||
assert len(mcp_config['tools']) == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@ -249,7 +251,11 @@ async def test_both_stdio_and_sse_mcp(
|
||||
assert obs_cat.exit_code == 0
|
||||
|
||||
mcp_action_fetch = MCPAction(
|
||||
name='fetch', arguments={'url': 'http://localhost:8000'}
|
||||
# NOTE: the tool name is `fetch_fetch` because the tool name is `fetch`
|
||||
# And FastMCP Proxy will pre-pend the server name (in this case, `fetch`)
|
||||
# to the tool name, so the full tool name becomes `fetch_fetch`
|
||||
name='fetch',
|
||||
arguments={'url': 'http://localhost:8000'},
|
||||
)
|
||||
obs_fetch = await runtime.call_tool_mcp(mcp_action_fetch)
|
||||
logger.info(obs_fetch, extra={'msg_type': 'OBSERVATION'})
|
||||
@ -304,7 +310,9 @@ async def test_microagent_and_one_stdio_mcp_in_config(
|
||||
logger.info(f'updated_config: {updated_config}')
|
||||
|
||||
# ======= Test the stdio server in the config =======
|
||||
mcp_action_sse = MCPAction(name='list_directory', arguments={'path': '/'})
|
||||
mcp_action_sse = MCPAction(
|
||||
name='filesystem_list_directory', arguments={'path': '/'}
|
||||
)
|
||||
obs_sse = await runtime.call_tool_mcp(mcp_action_sse)
|
||||
logger.info(obs_sse, extra={'msg_type': 'OBSERVATION'})
|
||||
assert isinstance(obs_sse, MCPObservation), (
|
||||
@ -332,7 +340,7 @@ async def test_microagent_and_one_stdio_mcp_in_config(
|
||||
assert obs_cat.exit_code == 0
|
||||
|
||||
mcp_action_fetch = MCPAction(
|
||||
name='fetch', arguments={'url': 'http://localhost:8000'}
|
||||
name='fetch_fetch', arguments={'url': 'http://localhost:8000'}
|
||||
)
|
||||
obs_fetch = await runtime.call_tool_mcp(mcp_action_fetch)
|
||||
logger.info(obs_fetch, extra={'msg_type': 'OBSERVATION'})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user