Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions examples/echo_mcp_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@
This example demonstrates:
1. Connecting to echo_env server
2. Listing available tools via MCP
3. Calling tools using both step() API and direct tool methods
3. Calling tools using the step() API
"""

import asyncio

try:
from core.env_server.types import CallToolAction, ListToolsAction
except ImportError:
from openenv_core.env_server.types import CallToolAction, ListToolsAction

from envs.echo_env import EchoEnv


Expand All @@ -23,17 +29,19 @@ async def main():
result = client.reset()
print(f" Reset result: {result.observation.metadata}\n")

# List available tools
# List available tools using step API
print("2. Listing available tools...")
tools = client.list_tools()
for tool in tools:
list_action = ListToolsAction()
list_result = client.step(list_action)
for tool in list_result.observation.tools:
print(f" - {tool['name']}: {tool['description']}")
print()

# Call echo_message tool using convenience method
# Call echo_message tool using step API
print("3. Calling echo_message tool...")
result = client.echo_message("Hello from MCP!")
print(f" Result: {result}\n")
call_action = CallToolAction(tool_name="echo_message", parameters={"message": "Hello from MCP!"})
call_result = client.step(call_action)
print(f" Result: {call_result.observation.result}\n")

# Check environment state
print("4. Checking environment state...")
Expand Down
4 changes: 2 additions & 2 deletions rfcs/RFC-003-implementation-journal.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ This journal tracks the implementation of RFC-003: MCP (Model Context Protocol)

#### Echo Env Conversion
- [x] Deprecate `src/envs/echo_env/models.py` (EchoAction deprecated)
- [x] Create `src/envs/echo_env/server/mcp_tools.py`
- [x] Create `src/envs/echo_env/server/mcp_server.py`
- [x] Define `echo_message` tool using FastMCP
- [x] Update `src/envs/echo_env/server/echo_environment.py`
- [x] Update `src/envs/echo_env/server/app.py` to initialize MCP
Expand Down Expand Up @@ -146,7 +146,7 @@ This journal tracks the implementation of RFC-003: MCP (Model Context Protocol)
- Added `mcp_server` parameter to `HTTPEnvServer` and `create_fastapi_app()`

5. **Echo Env Conversion**:
- Created `mcp_tools.py` with `echo_message` tool using FastMCP decorators
- Created `mcp_server.py` with `echo_message` tool using FastMCP decorators
- Rewrote `EchoEnvironment` to use MCP client instead of custom actions
- Updated `app.py` to initialize MCP server, client, and wire them together
- Deprecated `EchoAction` and `EchoObservation` in `models.py` with warnings
Expand Down
24 changes: 19 additions & 5 deletions src/core/env_server/http_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from __future__ import annotations

import asyncio
import json
import os
from concurrent.futures import ThreadPoolExecutor
from dataclasses import asdict
Expand All @@ -23,6 +22,7 @@
from fastapi import Body, FastAPI, Request

from .interfaces import Environment
from .mcp_environment import MCPEnvironment
from .types import Action, CallToolAction, ListToolsAction, Observation


Expand Down Expand Up @@ -148,17 +148,28 @@ async def mcp_endpoint(request: Request) -> Dict[str, Any]:
Returns:
JSON-RPC 2.0 response
"""
if self.env.mcp_client is None:
if not hasattr(self.env, "mcp_client") or self.env.mcp_client is None:
return {
"jsonrpc": "2.0",
"error": {
"code": -32600,
"code": -32603,
"message": "MCP server not configured for this environment",
},
"id": None,
}

body = await request.json()
try:
body = await request.json()
except (ValueError, TypeError):
return {
"jsonrpc": "2.0",
"error": {
"code": -32700,
"message": "Parse error: Invalid JSON",
},
"id": None,
}

method = body.get("method")
params = body.get("params", {})
request_id = body.get("id")
Expand Down Expand Up @@ -240,8 +251,11 @@ def _deserialize_action(self, action_data: Dict[str, Any]) -> Action:
return ListToolsAction()

elif action_type == "CallToolAction":
tool_name = action_data.get("tool_name")
if tool_name is None:
raise ValueError("Missing required field 'tool_name' for CallToolAction")
return CallToolAction(
tool_name=action_data["tool_name"],
tool_name=tool_name,
parameters=action_data.get("parameters", {}),
)

Expand Down
2 changes: 1 addition & 1 deletion src/core/env_server/mcp_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def __init__(self, mcp_server: Any):

self.mcp_server = mcp_server
self.mcp_client = Client(mcp_server)
super().__init__(mcp_client=self.mcp_client)
super().__init__()

def reset(self) -> Observation:
"""
Expand Down
3 changes: 1 addition & 2 deletions src/core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ packages = [
"openenv_core.containers",
"openenv_core.containers.runtime",
"openenv_core.env_server",
"openenv_core.tools",
"openenv_core.mcp"
"openenv_core.tools"
]
package-dir = {"openenv_core" = "."}
20 changes: 9 additions & 11 deletions src/envs/echo_env/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@
over HTTP using MCP actions.
"""

from typing import Any, Dict, List
from typing import Dict

try:
from core.client_types import StepResult
from core.env_server.types import (
CallToolAction,
CallToolObservation,
ListToolsAction,
ListToolsObservation,
Observation,
State,
Expand All @@ -29,7 +28,6 @@
from openenv_core.env_server.types import (
CallToolAction,
CallToolObservation,
ListToolsAction,
ListToolsObservation,
Observation,
State,
Expand All @@ -45,23 +43,23 @@ class EchoEnv(HTTPEnvClient[CallToolAction, Observation]):
methods to interact with it using MCP actions.

Example:
>>> from core.env_server.types import CallToolAction
>>> # Connect to a running server
>>> client = EchoEnv(base_url="http://localhost:8000")
>>> result = client.reset()
>>>
>>> # List available tools
>>> tools = client.list_tools()
>>> print(tools) # [{"name": "echo_message", ...}]
>>>
>>> # Call echo_message tool
>>> result = client.echo_message("Hello!")
>>> print(result["echoed_message"]) # "Hello!"
>>> # Call echo_message tool using step API
>>> action = CallToolAction(tool_name="echo_message", parameters={"message": "Hello!"})
>>> result = client.step(action)
>>> print(result.observation.result) # {"echoed_message": "Hello!"}

Example with Docker:
>>> from core.env_server.types import CallToolAction
>>> # Automatically start container and connect
>>> client = EchoEnv.from_docker_image("echo-env:latest")
>>> result = client.reset()
>>> result = client.echo_message("Test")
>>> action = CallToolAction(tool_name="echo_message", parameters={"message": "Test"})
>>> result = client.step(action)
"""

def _step_payload(self, action: CallToolAction) -> Dict:
Expand Down