# Gym MCP Client
A unified Python interface for working with both local and remote Gymnasium environments via [gym-mcp-server](https://github.com/haggaishachar/gym-mcp-server).
**Goal**: Write code once, seamlessly switch between local development and remote execution.
## Features
- 🎮 **Unified API**: Same Gymnasium interface for both local and remote environments
- 🔄 **Seamless Switching**: Change modes with a single parameter
- 🌐 **Remote Execution**: Connect to gym-mcp-server instances over HTTP
- 🔧 **Full Compatibility**: Supports all Gymnasium environment types (Box, Discrete, MultiBinary, etc.)
- 🐍 **Modern Python**: Python 3.12+ with complete type hints
- 📦 **Easy Setup**: Managed with uv for fast dependency management
- ✅ **Well Tested**: 14 comprehensive tests, all passing
## Installation
```bash
# Clone the repository
git clone <your-repo-url>
cd gym-mcp-client
# Install with uv (recommended)
uv sync
# Or with pip
pip install -e .
```
## Quick Start
### Local Mode
```python
from gym_mcp_client import GymMCPClient
# Create a local environment
env = GymMCPClient("CartPole-v1", mode="local")
# Use standard Gymnasium API
observation, info = env.reset(seed=42)
action = env.action_space.sample()
observation, reward, terminated, truncated, info = env.step(action)
env.close()
```
### Remote Mode
First, start a gym-mcp-server:
```bash
python -m gym_mcp_server --env CartPole-v1 --transport streamable-http --port 8000
```
Then connect to it:
```python
from gym_mcp_client import GymMCPClient
# Create a remote environment
env = GymMCPClient(
"CartPole-v1",
mode="remote",
gym_server_url="http://localhost:8000"
)
# Use the exact same API as local mode!
observation, info = env.reset(seed=42)
action = env.action_space.sample()
observation, reward, terminated, truncated, info = env.step(action)
env.close()
```
### Context Manager
```python
from gym_mcp_client import GymMCPClient
# Automatic cleanup
with GymMCPClient("CartPole-v1", mode="local") as env:
observation, info = env.reset()
action = env.action_space.sample()
observation, reward, terminated, truncated, info = env.step(action)
# env.close() called automatically
```
## Switching Between Modes
The main benefit: write once, run anywhere!
```python
import os
from gym_mcp_client import GymMCPClient
# Configuration from environment variables
MODE = os.getenv("GYM_MODE", "local")
SERVER_URL = os.getenv("GYM_SERVER_URL", "http://localhost:8000")
# Create environment based on mode
if MODE == "local":
env = GymMCPClient("CartPole-v1", mode="local")
else:
env = GymMCPClient(
"CartPole-v1",
mode="remote",
gym_server_url=SERVER_URL,
gym_server_key=os.getenv("GYM_API_KEY")
)
# Your code works the same regardless of mode!
observation, info = env.reset()
for _ in range(1000):
action = env.action_space.sample()
observation, reward, terminated, truncated, info = env.step(action)
if terminated or truncated:
observation, info = env.reset()
env.close()
```
Switch modes via command line:
```bash
# Run locally
export GYM_MODE=local
python your_training_script.py
# Run remotely
export GYM_MODE=remote
export GYM_SERVER_URL=http://gpu-server:8000
python your_training_script.py
```
## API Reference
### `GymMCPClient`
```python
GymMCPClient(
env_id: str,
mode: str = "local",
render_mode: str | None = None,
gym_server_url: str | None = None,
gym_server_key: str | None = None,
**kwargs
)
```
**Parameters:**
- `env_id`: Gymnasium environment ID (e.g., "CartPole-v1")
- `mode`: Either "local" or "remote"
- `render_mode`: Render mode ("rgb_array", "human", etc.)
- `gym_server_url`: URL of gym-mcp-server (required for remote mode)
- `gym_server_key`: Optional API key for authentication
- `**kwargs`: Additional arguments passed to `gym.make()` in local mode
**Methods:**
- `reset(seed=None, options=None)` → (observation, info)
- `step(action)` → (observation, reward, terminated, truncated, info)
- `render()` → render_output
- `close()` → None
**Properties:**
- `observation_space`: The observation space
- `action_space`: The action space
- `reward_range`: The reward range
- `metadata`: Environment metadata
## Architecture
```
┌─────────────────┐
│ Your Code │
└────────┬────────┘
│
▼
┌─────────────────┐
│ GymMCPClient │
└────────┬────────┘
│
┌────┴────┐
▼ ▼
┌────────┐ ┌──────────────┐
│ Local │ │ Remote │
│ Gym │ │ HTTP Client │
└────────┘ └──────┬───────┘
│
▼
┌──────────────┐
│ gym-mcp- │
│ server │
└──────┬───────┘
│
▼
┌────────┐
│ Gym │
│ Env │
└────────┘
```
### Supported Space Types
| Space Type | Local | Remote | Serialization |
|------------|-------|--------|---------------|
| Box | ✅ | ✅ | array ↔ list |
| Discrete | ✅ | ✅ | int ↔ int |
| MultiBinary | ✅ | ✅ | array ↔ list |
| MultiDiscrete | ✅ | ✅ | array ↔ list |
| Tuple | ✅ | ✅ | recursive |
| Dict | ✅ | ✅ | recursive |
## Examples
See the `examples/` directory:
- `local_example.py`: Local mode usage
- `remote_example.py`: Remote mode usage
- `context_manager_example.py`: Context manager pattern
Run examples:
```bash
# Local mode
uv run python examples/local_example.py
# Remote mode (start server first!)
python -m gym_mcp_server --env CartPole-v1 --transport streamable-http --port 8000
uv run python examples/remote_example.py
# Demo CLI tool
python main.py --mode local --episodes 3
```
## Development
### Setup
```bash
git clone <your-repo-url>
cd gym-mcp-client
make install
```
### Available Commands
```bash
make help # Show all commands
make test # Run test suite (14 tests)
make lint # Run ruff linter
make format # Format code with ruff
make typecheck # Run mypy type checker
make check # Run all checks (lint + typecheck + test)
make all # Format, then run all checks
make demo # Run local demo
make clean # Clean build artifacts
```
### Running Tests
```bash
make test
# Or: uv run pytest tests/ -v
# 14 tests, all passing ✅
```
## Use Cases
1. **Development → Production**: Develop locally, deploy remotely
2. **Distributed Training**: Multiple processes connecting to remote environments
3. **Resource Management**: Run expensive simulations on dedicated servers
4. **Testing**: Test locally before remote deployment
## Performance
### Local Mode
- **Overhead**: Minimal (thin wrapper)
- **Best for**: Development, testing, lightweight environments
### Remote Mode
- **Overhead**: HTTP round-trip (1-10ms on localhost)
- **Best for**: Expensive environments, distributed training, resource sharing
## Error Handling
```python
from gym_mcp_client import GymMCPClient
import httpx
try:
env = GymMCPClient(
"CartPole-v1",
mode="remote",
gym_server_url="http://localhost:8000"
)
observation, info = env.reset()
# ... your code ...
except ValueError:
# Invalid mode, missing URL, etc.
pass
except RuntimeError:
# Environment initialization failed, remote call failed
pass
except httpx.HTTPError:
# Network error (remote mode only)
pass
finally:
if 'env' in locals():
env.close()
```
## Troubleshooting
### Remote Connection Issues
1. Ensure the gym-mcp-server is running and accessible
2. Check URL is correct (including protocol: `http://` or `https://`)
3. Verify firewall/network settings
4. Check server logs for errors
### Environment Not Found
```bash
# For Atari environments
uv add "gymnasium[atari]"
# For Box2D environments
uv add "gymnasium[box2d]"
# For MuJoCo environments
uv add "gymnasium[mujoco]"
```
## Requirements
- Python 3.12+
- gymnasium >= 1.2.1
- httpx >= 0.28.1
- numpy >= 2.0.0
- gym-mcp-server (from GitHub)
## Contributing
Contributions are welcome! Please:
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run `make check` to verify all tests pass
5. Submit a Pull Request
See `CONTRIBUTING.md` for detailed guidelines.
## License
MIT License - see LICENSE file for details.
## Related Projects
- [gym-mcp-server](https://github.com/haggaishachar/gym-mcp-server) - MCP server for Gymnasium environments
- [Gymnasium](https://gymnasium.farama.org/) - RL environment standard
- [Model Context Protocol](https://modelcontextprotocol.io/) - Tool integration protocol
## Support
- **Issues**: Open a GitHub issue
- **Questions**: Start a GitHub discussion
- **Documentation**: See examples/ directory
---
**Status**: ✅ Production Ready | **Version**: 0.1.0 | **Python**: 3.12+
Raw data
{
"_id": null,
"home_page": null,
"name": "gym-mcp-client",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "agent, environment, gym, gymnasium, mcp, reinforcement-learning",
"author": "Haggai Shachar",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/a8/2e/3b366894d7a082f36f31828c0992ae2a5087b25cb20f8d4afe6aff5bb76c/gym_mcp_client-0.1.0.tar.gz",
"platform": null,
"description": "# Gym MCP Client\n\nA unified Python interface for working with both local and remote Gymnasium environments via [gym-mcp-server](https://github.com/haggaishachar/gym-mcp-server).\n\n**Goal**: Write code once, seamlessly switch between local development and remote execution.\n\n## Features\n\n- \ud83c\udfae **Unified API**: Same Gymnasium interface for both local and remote environments\n- \ud83d\udd04 **Seamless Switching**: Change modes with a single parameter\n- \ud83c\udf10 **Remote Execution**: Connect to gym-mcp-server instances over HTTP\n- \ud83d\udd27 **Full Compatibility**: Supports all Gymnasium environment types (Box, Discrete, MultiBinary, etc.)\n- \ud83d\udc0d **Modern Python**: Python 3.12+ with complete type hints\n- \ud83d\udce6 **Easy Setup**: Managed with uv for fast dependency management\n- \u2705 **Well Tested**: 14 comprehensive tests, all passing\n\n## Installation\n\n```bash\n# Clone the repository\ngit clone <your-repo-url>\ncd gym-mcp-client\n\n# Install with uv (recommended)\nuv sync\n\n# Or with pip\npip install -e .\n```\n\n## Quick Start\n\n### Local Mode\n\n```python\nfrom gym_mcp_client import GymMCPClient\n\n# Create a local environment\nenv = GymMCPClient(\"CartPole-v1\", mode=\"local\")\n\n# Use standard Gymnasium API\nobservation, info = env.reset(seed=42)\naction = env.action_space.sample()\nobservation, reward, terminated, truncated, info = env.step(action)\n\nenv.close()\n```\n\n### Remote Mode\n\nFirst, start a gym-mcp-server:\n\n```bash\npython -m gym_mcp_server --env CartPole-v1 --transport streamable-http --port 8000\n```\n\nThen connect to it:\n\n```python\nfrom gym_mcp_client import GymMCPClient\n\n# Create a remote environment\nenv = GymMCPClient(\n \"CartPole-v1\",\n mode=\"remote\",\n gym_server_url=\"http://localhost:8000\"\n)\n\n# Use the exact same API as local mode!\nobservation, info = env.reset(seed=42)\naction = env.action_space.sample()\nobservation, reward, terminated, truncated, info = env.step(action)\n\nenv.close()\n```\n\n### Context Manager\n\n```python\nfrom gym_mcp_client import GymMCPClient\n\n# Automatic cleanup\nwith GymMCPClient(\"CartPole-v1\", mode=\"local\") as env:\n observation, info = env.reset()\n action = env.action_space.sample()\n observation, reward, terminated, truncated, info = env.step(action)\n # env.close() called automatically\n```\n\n## Switching Between Modes\n\nThe main benefit: write once, run anywhere!\n\n```python\nimport os\nfrom gym_mcp_client import GymMCPClient\n\n# Configuration from environment variables\nMODE = os.getenv(\"GYM_MODE\", \"local\")\nSERVER_URL = os.getenv(\"GYM_SERVER_URL\", \"http://localhost:8000\")\n\n# Create environment based on mode\nif MODE == \"local\":\n env = GymMCPClient(\"CartPole-v1\", mode=\"local\")\nelse:\n env = GymMCPClient(\n \"CartPole-v1\",\n mode=\"remote\",\n gym_server_url=SERVER_URL,\n gym_server_key=os.getenv(\"GYM_API_KEY\")\n )\n\n# Your code works the same regardless of mode!\nobservation, info = env.reset()\nfor _ in range(1000):\n action = env.action_space.sample()\n observation, reward, terminated, truncated, info = env.step(action)\n if terminated or truncated:\n observation, info = env.reset()\n\nenv.close()\n```\n\nSwitch modes via command line:\n\n```bash\n# Run locally\nexport GYM_MODE=local\npython your_training_script.py\n\n# Run remotely\nexport GYM_MODE=remote\nexport GYM_SERVER_URL=http://gpu-server:8000\npython your_training_script.py\n```\n\n## API Reference\n\n### `GymMCPClient`\n\n```python\nGymMCPClient(\n env_id: str,\n mode: str = \"local\",\n render_mode: str | None = None,\n gym_server_url: str | None = None,\n gym_server_key: str | None = None,\n **kwargs\n)\n```\n\n**Parameters:**\n- `env_id`: Gymnasium environment ID (e.g., \"CartPole-v1\")\n- `mode`: Either \"local\" or \"remote\"\n- `render_mode`: Render mode (\"rgb_array\", \"human\", etc.)\n- `gym_server_url`: URL of gym-mcp-server (required for remote mode)\n- `gym_server_key`: Optional API key for authentication\n- `**kwargs`: Additional arguments passed to `gym.make()` in local mode\n\n**Methods:**\n- `reset(seed=None, options=None)` \u2192 (observation, info)\n- `step(action)` \u2192 (observation, reward, terminated, truncated, info)\n- `render()` \u2192 render_output\n- `close()` \u2192 None\n\n**Properties:**\n- `observation_space`: The observation space\n- `action_space`: The action space\n- `reward_range`: The reward range\n- `metadata`: Environment metadata\n\n## Architecture\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Your Code \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 GymMCPClient \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u250c\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2510\n \u25bc \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Local \u2502 \u2502 Remote \u2502\n\u2502 Gym \u2502 \u2502 HTTP Client \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u25bc\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 gym-mcp- \u2502\n \u2502 server \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u25bc\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 Gym \u2502\n \u2502 Env \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n### Supported Space Types\n\n| Space Type | Local | Remote | Serialization |\n|------------|-------|--------|---------------|\n| Box | \u2705 | \u2705 | array \u2194 list |\n| Discrete | \u2705 | \u2705 | int \u2194 int |\n| MultiBinary | \u2705 | \u2705 | array \u2194 list |\n| MultiDiscrete | \u2705 | \u2705 | array \u2194 list |\n| Tuple | \u2705 | \u2705 | recursive |\n| Dict | \u2705 | \u2705 | recursive |\n\n## Examples\n\nSee the `examples/` directory:\n- `local_example.py`: Local mode usage\n- `remote_example.py`: Remote mode usage\n- `context_manager_example.py`: Context manager pattern\n\nRun examples:\n\n```bash\n# Local mode\nuv run python examples/local_example.py\n\n# Remote mode (start server first!)\npython -m gym_mcp_server --env CartPole-v1 --transport streamable-http --port 8000\nuv run python examples/remote_example.py\n\n# Demo CLI tool\npython main.py --mode local --episodes 3\n```\n\n## Development\n\n### Setup\n\n```bash\ngit clone <your-repo-url>\ncd gym-mcp-client\nmake install\n```\n\n### Available Commands\n\n```bash\nmake help # Show all commands\nmake test # Run test suite (14 tests)\nmake lint # Run ruff linter\nmake format # Format code with ruff\nmake typecheck # Run mypy type checker\nmake check # Run all checks (lint + typecheck + test)\nmake all # Format, then run all checks\nmake demo # Run local demo\nmake clean # Clean build artifacts\n```\n\n### Running Tests\n\n```bash\nmake test\n# Or: uv run pytest tests/ -v\n# 14 tests, all passing \u2705\n```\n\n## Use Cases\n\n1. **Development \u2192 Production**: Develop locally, deploy remotely\n2. **Distributed Training**: Multiple processes connecting to remote environments\n3. **Resource Management**: Run expensive simulations on dedicated servers\n4. **Testing**: Test locally before remote deployment\n\n## Performance\n\n### Local Mode\n- **Overhead**: Minimal (thin wrapper)\n- **Best for**: Development, testing, lightweight environments\n\n### Remote Mode\n- **Overhead**: HTTP round-trip (1-10ms on localhost)\n- **Best for**: Expensive environments, distributed training, resource sharing\n\n## Error Handling\n\n```python\nfrom gym_mcp_client import GymMCPClient\nimport httpx\n\ntry:\n env = GymMCPClient(\n \"CartPole-v1\",\n mode=\"remote\",\n gym_server_url=\"http://localhost:8000\"\n )\n observation, info = env.reset()\n # ... your code ...\nexcept ValueError:\n # Invalid mode, missing URL, etc.\n pass\nexcept RuntimeError:\n # Environment initialization failed, remote call failed\n pass\nexcept httpx.HTTPError:\n # Network error (remote mode only)\n pass\nfinally:\n if 'env' in locals():\n env.close()\n```\n\n## Troubleshooting\n\n### Remote Connection Issues\n\n1. Ensure the gym-mcp-server is running and accessible\n2. Check URL is correct (including protocol: `http://` or `https://`)\n3. Verify firewall/network settings\n4. Check server logs for errors\n\n### Environment Not Found\n\n```bash\n# For Atari environments\nuv add \"gymnasium[atari]\"\n\n# For Box2D environments\nuv add \"gymnasium[box2d]\"\n\n# For MuJoCo environments\nuv add \"gymnasium[mujoco]\"\n```\n\n## Requirements\n\n- Python 3.12+\n- gymnasium >= 1.2.1\n- httpx >= 0.28.1\n- numpy >= 2.0.0\n- gym-mcp-server (from GitHub)\n\n## Contributing\n\nContributions are welcome! Please:\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Run `make check` to verify all tests pass\n5. Submit a Pull Request\n\nSee `CONTRIBUTING.md` for detailed guidelines.\n\n## License\n\nMIT License - see LICENSE file for details.\n\n## Related Projects\n\n- [gym-mcp-server](https://github.com/haggaishachar/gym-mcp-server) - MCP server for Gymnasium environments\n- [Gymnasium](https://gymnasium.farama.org/) - RL environment standard\n- [Model Context Protocol](https://modelcontextprotocol.io/) - Tool integration protocol\n\n## Support\n\n- **Issues**: Open a GitHub issue\n- **Questions**: Start a GitHub discussion\n- **Documentation**: See examples/ directory\n\n---\n\n**Status**: \u2705 Production Ready | **Version**: 0.1.0 | **Python**: 3.12+\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A unified interface for local and remote Gymnasium environments via gym-mcp-server",
"version": "0.1.0",
"project_urls": {
"Author LinkedIn": "https://www.linkedin.com/in/haggaishachar/",
"Documentation": "https://agentring.ai",
"Homepage": "https://agentring.ai",
"Issues": "https://github.com/Agent-Ring/gym-mcp-client/issues",
"Repository": "https://github.com/Agent-Ring/gym-mcp-client"
},
"split_keywords": [
"agent",
" environment",
" gym",
" gymnasium",
" mcp",
" reinforcement-learning"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "45ae0fdecda9f88359cd8ecd60c71549116e30a063102a04fc50d7b3460a3995",
"md5": "c772dd79455e654a415bd05179b12651",
"sha256": "2c1fb0e89af4aa05b74f1c6e800db1084459e54aadc9dbff13bf69d0e361453b"
},
"downloads": -1,
"filename": "gym_mcp_client-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c772dd79455e654a415bd05179b12651",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 9620,
"upload_time": "2025-11-01T13:54:48",
"upload_time_iso_8601": "2025-11-01T13:54:48.072548Z",
"url": "https://files.pythonhosted.org/packages/45/ae/0fdecda9f88359cd8ecd60c71549116e30a063102a04fc50d7b3460a3995/gym_mcp_client-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "a82e3b366894d7a082f36f31828c0992ae2a5087b25cb20f8d4afe6aff5bb76c",
"md5": "b4c43d25b1cd05484e6cf02bafcdcde6",
"sha256": "f432bafcd227da9f627f66d8e8cc6bd14f8fe3b8df451e600f52cb0d3791f17c"
},
"downloads": -1,
"filename": "gym_mcp_client-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "b4c43d25b1cd05484e6cf02bafcdcde6",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 12069,
"upload_time": "2025-11-01T13:54:49",
"upload_time_iso_8601": "2025-11-01T13:54:49.607127Z",
"url": "https://files.pythonhosted.org/packages/a8/2e/3b366894d7a082f36f31828c0992ae2a5087b25cb20f8d4afe6aff5bb76c/gym_mcp_client-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-11-01 13:54:49",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Agent-Ring",
"github_project": "gym-mcp-client",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "gym-mcp-client"
}