Repository navigation
feat(mcp): Enable code mode for MCP environments - #351
Conversation
ae04170 to
eb52a6f
Compare
Greptile OverviewGreptile SummaryThis PR implements MCP code mode (CodeAct) per RFC 003, enabling agents to write Python code blocks that directly call environment tools as functions, avoiding double-marshaling overhead for local MCP servers. Key Changes
Alignment AssessmentThis implementation correctly follows RFC 003's design for code mode and maintains the dual API boundary:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Agent
participant InfraClient as Infrastructure<br/>(EnvClient/MCPToolClient)
participant HTTPServer as HTTP Server<br/>(http_server.py)
participant MCPEnv as MCP Environment<br/>(mcp_environment.py)
participant MCPServer as MCP Server<br/>(FastMCP)
Note over Agent,MCPServer: Production Mode: Direct MCP Access (bypasses step API)
Agent->>HTTPServer: POST /mcp<br/>{"jsonrpc":"2.0", "method":"tools/list"}
HTTPServer->>MCPEnv: _env_factory()
HTTPServer->>MCPEnv: list_tools()
MCPEnv->>MCPServer: list_tools()
MCPServer-->>MCPEnv: [tools]
MCPEnv-->>HTTPServer: [tools]
HTTPServer->>MCPEnv: close()
HTTPServer-->>Agent: {"result": {"tools": [...]}}
Note over Agent,MCPServer: Tool-Calling Mode (traditional MCP)
InfraClient->>HTTPServer: POST /step<br/>CallToolAction("add", {a:1, b:2})
HTTPServer->>MCPEnv: step(CallToolAction)
MCPEnv->>MCPEnv: _handle_call_tool()
MCPEnv->>MCPServer: call_tool("add", {a:1, b:2})
MCPServer-->>MCPEnv: result=3
MCPEnv->>MCPEnv: _compute_reward()
MCPEnv-->>HTTPServer: CallToolObservation(result=3, reward=0.0)
HTTPServer-->>InfraClient: StepResponse
Note over Agent,MCPServer: Code Mode (CodeAct pattern - local optimization)
InfraClient->>HTTPServer: POST /step<br/>CodeAction("result = add(1, 2)")
HTTPServer->>MCPEnv: step(CodeAction)
MCPEnv->>MCPEnv: execute_code()
MCPEnv->>MCPEnv: get_callables()
Note over MCPEnv: Local: Direct Python call<br/>Remote: JSON-RPC wrapper
MCPEnv->>MCPServer: Direct fn call (local)<br/>OR JSON-RPC (remote)
MCPServer-->>MCPEnv: result=3
MCPEnv->>MCPEnv: _compute_reward()
MCPEnv-->>HTTPServer: Observation(metadata={"result": 3}, reward=0.0)
HTTPServer-->>InfraClient: StepResponse
|
| result = self.step(ListToolsAction()) | ||
| self._tools_cache = result.observation.tools | ||
| return self._tools_cache | ||
|
|
||
| result = self.step(ListToolsAction()) | ||
| self._tools_cache = result.observation.tools | ||
| return self._tools_cache | ||
| result = self.step(ListToolsAction()) | ||
| self._tools_cache = result.observation.tools | ||
| return self._tools_cache |
There was a problem hiding this comment.
duplicated code - these 3 identical blocks should be removed
| result = self.step(ListToolsAction()) | |
| self._tools_cache = result.observation.tools | |
| return self._tools_cache | |
| result = self.step(ListToolsAction()) | |
| self._tools_cache = result.observation.tools | |
| return self._tools_cache | |
| result = self.step(ListToolsAction()) | |
| self._tools_cache = result.observation.tools | |
| return self._tools_cache | |
| result = self.step(ListToolsAction()) | |
| self._tools_cache = result.observation.tools | |
| return self._tools_cache |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/openenv/core/mcp_client.py
Line: 139:148
Comment:
duplicated code - these 3 identical blocks should be removed
```suggestion
result = self.step(ListToolsAction())
self._tools_cache = result.observation.tools
return self._tools_cache
```
How can I resolve this? If you propose a fix, please make it concise.db48f93 to
c36a37d
Compare
burtenshaw
left a comment
There was a problem hiding this comment.
Nice works again. Just some small queries. In general, I think we could make this code a bit easier to understand for users. For example, I think the user will a simple overview of how these methods play together. For example, a schema like this could help:
┌─────────────────────────────────────────────────────────┐
│ HTTPEnvServer │
├─────────────────────────────────────────────────────────┤
│ Simulation Mode: │
│ /ws → OpenEnv protocol (reset/step/state) │
│ /mcp → MCP JSON-RPC (tools/list, tools/call) │
│ /reset, /step, /state → HTTP (enabled) │
├─────────────────────────────────────────────────────────┤
│ Production Mode: │
│ /ws → OpenEnv protocol (disabled/blocked) │
│ /mcp → MCP JSON-RPC (enabled) │
│ /reset, /step, /state → HTTP (disabled) │
└─────────────────────────────────────────────────────────┘
Client Classes:
GenericEnvClient → /ws (simulation only)
MCPToolClient → /mcp (production only)
Also, I think we might want to rename production to something like mcp_only to make it more obvious.
|
|
||
| return callables | ||
|
|
||
| def execute_code(self, code: str) -> Observation: |
There was a problem hiding this comment.
Should we break up these methods to have a function that deal with the details of executing code and return erorrs nicely. This should impact learning too. For example, unsloth have some nice examples here.
| name=action.tool_name, arguments=action.arguments | ||
| ) | ||
| break | ||
| except: |
There was a problem hiding this comment.
This might be hard to debug. We could update a string with errors and log in a finally if result is None.
| Tool result. | ||
| """ | ||
| # For mocks, call directly | ||
| if ( |
There was a problem hiding this comment.
Does this logic not require parenthese to open?
if (
hasattr(server, "list_tools")
and (not hasattr(type(server), "__name__") or "FastMCP" not in type(server).__name__)
):f9d4349 to
9c2de6b
Compare
|
One thing that concerns me about this implementation is the lack of types. I've opened a PR to use types for flats and json rpc objects. This also allows us to refactor the code to be easier to maintain. |
Enable code mode for MCP environments and add direct MCP JSON-RPC access for production deployments, rebased on top of mode-aware tool registration from PR #360. Code mode (mcp_environment.py): - Add supports_code_mode property - Add get_callables() to extract Python callables from FastMCP tools, integrated with mode-aware tool filtering - Add execute_code() for CodeAct pattern (agents write Python code that calls tools directly, avoiding JSON-RPC overhead) Production MCP endpoints (http_server.py): - Add HTTP POST /mcp endpoint for direct MCP JSON-RPC access - Bypasses step() overhead for production inference - Supports tools/list and tools/call with proper error handling Client support (mcp_client.py): - Add use_production_mode flag for HTTP /mcp endpoint routing - Add architecture overview documentation Tests: 149 new/updated tests covering code mode, production routes, WebSocket MCP, reserved tool names, and error handling.
9c2de6b to
6d1dbb2
Compare
|
@burtenshaw this PR has been in git hell for a while :D Let me land this one, and we move your PR to be on top of main for simplicity |
Summary
Enable code mode for MCP environments and add direct MCP JSON-RPC access for production deployments. Rebased on top of mode-aware tool registration from PR #360.
Code mode (
mcp_environment.py):supports_code_modeproperty for capability detectionget_callables()extracts Python callables from FastMCP tools, integrated with mode-aware tool filteringexecute_code()enables the CodeAct pattern — agents write Python code that calls tools directly, avoiding JSON-RPC overheadProduction MCP endpoints (
http_server.py):/mcpendpoint for direct MCP JSON-RPC accessstep()overhead for production inferencetools/listandtools/callwith proper JSON-RPC error handlingClient support (
mcp_client.py):use_production_modeflag for HTTP/mcpendpoint routingChanges from previous version
This PR was rebased on top of
main(which now includes PR #360's mode-aware tool registration). Key integration changes:get_callables()now integrates with mode-aware tools (filters by current mode)FastMCPservers instead ofMagicMock_compute_reward()methodTest plan
ruff format+ruff check)Closes #347