Skip to content

Commit 40dcea2

Browse files
CopilotDarktex
andauthored
Address code review feedback for MCP implementation (#226)
* Initial plan * Address code review feedback from copilot-pull-request-reviewer Co-authored-by: Darktex <890615+Darktex@users.noreply.github.com> * Address secondary code review comments Co-authored-by: Darktex <890615+Darktex@users.noreply.github.com> * Remove list_tools and echo_message convenience methods per review feedback Co-authored-by: Darktex <890615+Darktex@users.noreply.github.com> * Address code review feedback: add import examples and use isinstance Co-authored-by: Darktex <890615+Darktex@users.noreply.github.com> * Remove unnecessary isinstance check in echo_mcp_demo.py Co-authored-by: Darktex <890615+Darktex@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Darktex <890615+Darktex@users.noreply.github.com>
1 parent 84a2919 commit 40dcea2

File tree

6 files changed

+47
-28
lines changed

6 files changed

+47
-28
lines changed

examples/echo_mcp_demo.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44
This example demonstrates:
55
1. Connecting to echo_env server
66
2. Listing available tools via MCP
7-
3. Calling tools using both step() API and direct tool methods
7+
3. Calling tools using the step() API
88
"""
99

1010
import asyncio
11+
12+
try:
13+
from core.env_server.types import CallToolAction, ListToolsAction
14+
except ImportError:
15+
from openenv_core.env_server.types import CallToolAction, ListToolsAction
16+
1117
from envs.echo_env import EchoEnv
1218

1319

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

26-
# List available tools
32+
# List available tools using step API
2733
print("2. Listing available tools...")
28-
tools = client.list_tools()
29-
for tool in tools:
34+
list_action = ListToolsAction()
35+
list_result = client.step(list_action)
36+
for tool in list_result.observation.tools:
3037
print(f" - {tool['name']}: {tool['description']}")
3138
print()
3239

33-
# Call echo_message tool using convenience method
40+
# Call echo_message tool using step API
3441
print("3. Calling echo_message tool...")
35-
result = client.echo_message("Hello from MCP!")
36-
print(f" Result: {result}\n")
42+
call_action = CallToolAction(tool_name="echo_message", parameters={"message": "Hello from MCP!"})
43+
call_result = client.step(call_action)
44+
print(f" Result: {call_result.observation.result}\n")
3745

3846
# Check environment state
3947
print("4. Checking environment state...")

rfcs/RFC-003-implementation-journal.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ This journal tracks the implementation of RFC-003: MCP (Model Context Protocol)
9696

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

148148
5. **Echo Env Conversion**:
149-
- Created `mcp_tools.py` with `echo_message` tool using FastMCP decorators
149+
- Created `mcp_server.py` with `echo_message` tool using FastMCP decorators
150150
- Rewrote `EchoEnvironment` to use MCP client instead of custom actions
151151
- Updated `app.py` to initialize MCP server, client, and wire them together
152152
- Deprecated `EchoAction` and `EchoObservation` in `models.py` with warnings

src/core/env_server/http_server.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from __future__ import annotations
1515

1616
import asyncio
17-
import json
1817
import os
1918
from concurrent.futures import ThreadPoolExecutor
2019
from dataclasses import asdict
@@ -23,6 +22,7 @@
2322
from fastapi import Body, FastAPI, Request
2423

2524
from .interfaces import Environment
25+
from .mcp_environment import MCPEnvironment
2626
from .types import Action, CallToolAction, ListToolsAction, Observation
2727

2828

@@ -148,17 +148,28 @@ async def mcp_endpoint(request: Request) -> Dict[str, Any]:
148148
Returns:
149149
JSON-RPC 2.0 response
150150
"""
151-
if self.env.mcp_client is None:
151+
if not hasattr(self.env, "mcp_client") or self.env.mcp_client is None:
152152
return {
153153
"jsonrpc": "2.0",
154154
"error": {
155-
"code": -32600,
155+
"code": -32603,
156156
"message": "MCP server not configured for this environment",
157157
},
158158
"id": None,
159159
}
160160

161-
body = await request.json()
161+
try:
162+
body = await request.json()
163+
except (ValueError, TypeError):
164+
return {
165+
"jsonrpc": "2.0",
166+
"error": {
167+
"code": -32700,
168+
"message": "Parse error: Invalid JSON",
169+
},
170+
"id": None,
171+
}
172+
162173
method = body.get("method")
163174
params = body.get("params", {})
164175
request_id = body.get("id")
@@ -240,8 +251,11 @@ def _deserialize_action(self, action_data: Dict[str, Any]) -> Action:
240251
return ListToolsAction()
241252

242253
elif action_type == "CallToolAction":
254+
tool_name = action_data.get("tool_name")
255+
if tool_name is None:
256+
raise ValueError("Missing required field 'tool_name' for CallToolAction")
243257
return CallToolAction(
244-
tool_name=action_data["tool_name"],
258+
tool_name=tool_name,
245259
parameters=action_data.get("parameters", {}),
246260
)
247261

src/core/env_server/mcp_environment.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def __init__(self, mcp_server: Any):
6767

6868
self.mcp_server = mcp_server
6969
self.mcp_client = Client(mcp_server)
70-
super().__init__(mcp_client=self.mcp_client)
70+
super().__init__()
7171

7272
def reset(self) -> Observation:
7373
"""

src/core/pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ packages = [
4444
"openenv_core.containers",
4545
"openenv_core.containers.runtime",
4646
"openenv_core.env_server",
47-
"openenv_core.tools",
48-
"openenv_core.mcp"
47+
"openenv_core.tools"
4948
]
5049
package-dir = {"openenv_core" = "."}

src/envs/echo_env/client.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@
1111
over HTTP using MCP actions.
1212
"""
1313

14-
from typing import Any, Dict, List
14+
from typing import Dict
1515

1616
try:
1717
from core.client_types import StepResult
1818
from core.env_server.types import (
1919
CallToolAction,
2020
CallToolObservation,
21-
ListToolsAction,
2221
ListToolsObservation,
2322
Observation,
2423
State,
@@ -29,7 +28,6 @@
2928
from openenv_core.env_server.types import (
3029
CallToolAction,
3130
CallToolObservation,
32-
ListToolsAction,
3331
ListToolsObservation,
3432
Observation,
3533
State,
@@ -45,23 +43,23 @@ class EchoEnv(HTTPEnvClient[CallToolAction, Observation]):
4543
methods to interact with it using MCP actions.
4644
4745
Example:
46+
>>> from core.env_server.types import CallToolAction
4847
>>> # Connect to a running server
4948
>>> client = EchoEnv(base_url="http://localhost:8000")
5049
>>> result = client.reset()
5150
>>>
52-
>>> # List available tools
53-
>>> tools = client.list_tools()
54-
>>> print(tools) # [{"name": "echo_message", ...}]
55-
>>>
56-
>>> # Call echo_message tool
57-
>>> result = client.echo_message("Hello!")
58-
>>> print(result["echoed_message"]) # "Hello!"
51+
>>> # Call echo_message tool using step API
52+
>>> action = CallToolAction(tool_name="echo_message", parameters={"message": "Hello!"})
53+
>>> result = client.step(action)
54+
>>> print(result.observation.result) # {"echoed_message": "Hello!"}
5955
6056
Example with Docker:
57+
>>> from core.env_server.types import CallToolAction
6158
>>> # Automatically start container and connect
6259
>>> client = EchoEnv.from_docker_image("echo-env:latest")
6360
>>> result = client.reset()
64-
>>> result = client.echo_message("Test")
61+
>>> action = CallToolAction(tool_name="echo_message", parameters={"message": "Test"})
62+
>>> result = client.step(action)
6563
"""
6664

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

0 commit comments

Comments
 (0)