Compare commits

...

10 Commits

Author SHA1 Message Date
openhands
c5f31b643e Resolve merge conflict in MCP Proxy Manager 2025-06-10 15:51:28 +00:00
Xingyao Wang
5730db327a Merge branch 'main' into docs/update-runtime-testing-documentation 2025-06-07 13:45:52 -04:00
Xingyao Wang
24a03738a8 Merge branch 'main' into docs/update-runtime-testing-documentation 2025-06-05 17:21:21 -04:00
Xingyao Wang
3a093c13b8 revert stuff 2025-06-05 17:20:55 -04:00
openhands
e367ced954 docs: update runtime testing documentation and environment setup 2025-06-05 21:18:05 +00:00
Xingyao Wang
4c2c1bd8eb Merge branch 'main' into refactor-mcprouter-to-fastmcp 2025-06-05 15:09:19 -04:00
openhands
20b1f37fbd Fix test_default_activated_tools to match new MCP config structure 2025-06-03 19:13:16 +00:00
openhands
2bae39ef30 Refactor MCP Proxy implementation to use MCPProxyManager
- Created new MCPProxyManager class to encapsulate proxy functionality
- Removed file-based configuration in favor of in-memory configuration
- Updated action_execution_server.py to use the new MCPProxyManager
- Added comprehensive documentation for the new module
- Improved error handling and logging
2025-06-03 18:32:47 +00:00
Xingyao Wang
9f8bcf8729 Merge commit 'a348840534dc10f20e57e80b6a80a08ac7a030f0' into refactor-mcprouter-to-fastmcp 2025-06-03 14:16:13 -04:00
openhands
137eec4dd1 Refactor MCPRouter to use FastMCP Proxy 2025-06-02 20:02:12 +00:00
2 changed files with 96 additions and 20 deletions

View File

@@ -2,8 +2,6 @@ This repository contains the code for OpenHands, an automated AI software engine
(in the `openhands` directory) and React frontend (in the `frontend` directory).
## General Setup:
To set up the entire repo, including frontend and backend, run `make build`.
You don't need to do this unless the user asks you to, or if you're trying to run the entire application.
IMPORTANT: Before making any changes to the codebase, ALWAYS run `make install-pre-commit-hooks` to ensure pre-commit hooks are properly installed.
@@ -21,13 +19,91 @@ then re-run the command to ensure it passes. Common issues include:
- Trailing whitespace
- Missing newlines at end of files
## Testing and Debugging
### Environment Setup for Testing
- Run `make build` to install all dependencies (only necessary for running tests):
```bash
make build
```
**IMPORTANT**: When using `execute_bash` to run `make build` or similar long-running commands, set the `timeout` parameter to a high value (e.g., 600 seconds):
```
execute_bash(command="make build", timeout=600)
```
#### Docker Installation
**NOTE: Docker installation is ONLY required for running runtime tests with the Docker runtime.**
- Install Docker on Debian-based systems:
```bash
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
```
- Start Docker daemon (in container environments without systemd):
```bash
sudo dockerd > /tmp/docker.log 2>&1 & sleep 5
```
- Verify Docker installation:
```bash
sudo docker run hello-world
```
#### Development Environment Setup
- Before running `make run`, ensure netcat is installed:
```bash
sudo apt-get install -y netcat-openbsd
```
### Unit Tests
- All unit tests are in `tests/unit/test_*.py`
- To test new code, run `poetry run pytest tests/unit/test_xxx.py` where `xxx` is the appropriate file for the current functionality
- Write all tests with pytest
### Runtime Tests
- Runtime tests are in `tests/runtime/test_*.py`
- Run tests with different runtime implementations by setting the `TEST_RUNTIME` environment variable:
```bash
# Use Docker runtime (default)
DEBUG=1 poetry run pytest -vvxss tests/runtime/test_bash.py
# Use CLI runtime (more reliable in some environments)
DEBUG=1 TEST_RUNTIME=cli poetry run pytest -vvxss tests/runtime/test_bash.py
# Run a specific test
DEBUG=1 TEST_RUNTIME=cli poetry run pytest -vvxss tests/runtime/test_bash.py::test_bash_server
```
- **IMPORTANT**: Runtime tests can take a long time to run, especially when building Docker images. Set a high timeout value:
```
execute_bash(command="DEBUG=1 poetry run pytest -vvxss tests/runtime/test_bash.py", timeout=600)
```
- The `DEBUG=1` flag enables more verbose logging
- The `-vvxss` flags make the test output more verbose and stop after the first failure
### Debugging Docker Issues
- Check Docker container status:
```bash
sudo docker ps -a
```
- View Docker logs:
```bash
sudo docker logs <container_id>
```
- Check Docker daemon logs:
```bash
sudo cat /tmp/docker.log | tail -n 100
```
- Check OpenHands logs:
```bash
cat logs/openhands_*.log | grep -i error | tail -n 20
```
## Repository Structure
Backend:
- Located in the `openhands` directory
- Testing:
- All tests are in `tests/unit/test_*.py`
- To test new code, run `poetry run pytest tests/unit/test_xxx.py` where `xxx` is the appropriate file for the current functionality
- Write all tests with pytest
Frontend:
- Located in the `frontend` directory
@@ -50,6 +126,13 @@ Frontend:
If you are starting a pull request (PR), please follow the template in `.github/pull_request_template.md`.
## Runtime Architecture
- OpenHands uses a Docker-based runtime for secure execution of agent actions
- The runtime builds a custom Docker image based on a specified base image
- The image includes OpenHands-specific code and the runtime client
- The runtime client executes actions in the sandboxed environment and returns observations
- More details in the [runtime architecture documentation](https://docs.all-hands.dev/usage/architecture/runtime)
## Implementation Details
These details may or may not be useful for your current task.
@@ -80,4 +163,4 @@ These details may or may not be useful for your current task.
- Add the translation key to `frontend/src/i18n/declaration.ts`
2. Add the setting to the backend:
- Add the setting to the `Settings` model in `openhands/storage/data_models/settings.py`
- Update any relevant backend code to apply the setting (e.g., in session creation)
- Update any relevant backend code to apply the setting (e.g., in session creation)

View File

@@ -36,7 +36,6 @@ class MCPProxyManager:
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
@@ -59,7 +58,7 @@ class MCPProxyManager:
"""
if len(self.config['mcpServers']) == 0:
logger.info(
f"No MCP servers configured for FastMCP Proxy, skipping initialization."
'No MCP servers configured for FastMCP Proxy, skipping initialization.'
)
return None
@@ -70,7 +69,7 @@ class MCPProxyManager:
api_key=self.api_key,
)
logger.info(f"FastMCP Proxy initialized successfully")
logger.info('FastMCP Proxy initialized successfully')
async def mount_to_app(
self, app: FastAPI, allow_origins: Optional[list[str]] = None
@@ -83,9 +82,7 @@ class MCPProxyManager:
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."
)
logger.info('No MCP servers configured for FastMCP Proxy, skipping mount.')
return
if not self.proxy:
@@ -101,8 +98,7 @@ class MCPProxyManager:
app.routes.remove('/mcp')
app.mount('/', mcp_app)
logger.info(f"Mounted FastMCP Proxy app at /mcp")
logger.info('Mounted FastMCP Proxy app at /mcp')
async def update_and_remount(
self,
@@ -119,13 +115,10 @@ class MCPProxyManager:
Args:
app: FastAPI application to mount to
tools: List of tool configurations
stdio_servers: List of stdio server configurations
allow_origins: List of allowed origins for CORS
"""
tools = {
t.name: t.model_dump()
for t in stdio_servers
}
tools = {t.name: t.model_dump() for t in stdio_servers}
self.config['mcpServers'] = tools
del self.proxy