Skip to content

Fix local Echo example MCP usage#990

Open
burtenshaw wants to merge 1 commit into
mainfrom
codex/fix-local-echo-example-mcp
Open

Fix local Echo example MCP usage#990
burtenshaw wants to merge 1 commit into
mainfrom
codex/fix-local-echo-example-mcp

Conversation

@burtenshaw

Copy link
Copy Markdown
Collaborator

Updates the local Echo example to use the supported MCP client methods and sync wrapper against a running Echo server.

@bot-ci-comment

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@burtenshaw burtenshaw added bug Something isn't working size: small Small pull request labels Jul 17, 2026 — with Cursor

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alignment Review Report

Reviewed the migration of examples/local_echo_env.py from the removed EchoAction API to the MCP tool-calling API. I verified everything end-to-end against a live server (python -m echo_env.server.app).

Automated Checks

  • Lint: PASS (this PR). examples/local_echo_env.py passes ruff check, ruff format --check, and usort check, and byte-compiles. The lint.sh hook exits non-zero, but every file it flags is pre-existing formatting drift in unrelated envs (opencode_env, chat_env, repl_env, textarena_env, …) — none is this PR's file. Note examples/ is outside both CI (src/ tests/) and the hook's scope, so I checked the changed file directly.
  • Debug code: CLEAN. check-debug.sh only scans src/; all hits are pre-existing there. The print() calls in this file are intentional demo output.

Open RFCs Context

  • RFC 003 — MCP Support (In Review; @Darktex, @pankit-eng): directly relevant, and this change aligns with it — the example now drives the env purely through MCP tools (list_tools() / call_tool()), i.e. the "MCP as universal standard" direction. No conflict.
  • RFC 010 ("ECHO") is env-token world-modeling, unrelated to echo_env.

Tier 1: Fixes Required

None. Verified against a running server:

  • The old example was broken at import time — from echo_env import EchoAction raises ImportError (echo_env exports only EchoEnv, CallToolAction, ListToolsAction). This PR fixes a genuinely non-running example.
  • The new example runs cleanly (exit 0). Output confirmed correct: reset → status='ready', message='Echo environment ready!', reward=0.0, done=False; both tools listed with descriptions; echoes correct; lengths 13 / 24 / 16.

Tier 2: Alignment Discussion

Principle Conflicts

None identified. Client-only example (imports EchoEnv, never server/) → client-server separation intact. Uses the Gym-style reset() plus MCP tools; no external reward computation introduced.

RFC Conflicts

None identified (aligns with RFC 003, above).

Summary

  • 0 mechanical issues to fix
  • 0 alignment points for human review
  • 0 RFC conflicts

Nice cleanup. Beyond fixing the import, the migration also sidesteps two known echo_env gotchas: it reads results via call_tool (which unwraps the FastMCP envelope to .data) rather than the raw observation.result, and it drops the per-tool-call reward print (tool steps return reward=None, not 0.0 — only reset() is 0.0).

Optional, non-blocking (out of scope here): a few other spots still use the pre-MCP echo observation shape (echoed_message / message_length) — notably src/openenv/core/containers/test_local_docker_provider.py (already TODO-marked for removal and Docker-gated) and the env-generation templates under src/openenv/cli/templates/openenv_env/ and .claude/skills/generate-openenv-env/assets/. Flagging only for awareness.

Open in Web View Automation 

Sent by Cursor Automation: Pre-review

print("\n1. Reset:")
result = client.reset()
print(f" Message: {result.observation.echoed_message}")
print(f" Status: {result.observation.metadata.get('status')}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified correct against a running server: EchoEnvironment.reset() returns Observation(reward=0.0, metadata={'status': 'ready', 'message': 'Echo environment ready!'}), so both metadata.get(...) lookups resolve as intended (printed Status: ready / Message: Echo environment ready!).


for i, msg in enumerate(messages, 1):
result = client.step(EchoAction(message=msg))
echoed = client.call_tool("echo_message", message=msg)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good use of the call_tool convenience method — it unwraps the FastMCP result envelope to .data, so echoed is the plain string and with_length (line 67) is the plain {'message': ..., 'length': N} dict (verified: lengths 13/24/16). This avoids the raw observation.result envelope gotcha, and dropping the per-call reward print is correct since MCP tool steps return reward=None (only reset() is 0.0).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working codex codex-automation size: small Small pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant