Skip to content

feat(mcp): run on mcp 1 and mcp 2, so a trainer can install vLLM 0.30 and later - #357

Draft
earakely-scale wants to merge 4 commits into
mainfrom
edgararakelyan/fd-4000-mcp-2
Draft

earakely-scale wants to merge 4 commits into
mainfrom
edgararakelyan/fd-4000-mcp-2

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

What and why

vLLM 0.30 and later require mcp>=2. agent-env and its protocol package pinned mcp<2, so a trainer with agent-env installed was held to vLLM 0.29. TRL 1.16 allows vLLM 0.22–0.31, and its minimum keeps rising. On the current release, agentenv-framework with trl[vllm] and vllm>=0.30 has no solution; on this branch it resolves on Linux to TRL 1.16, vLLM 0.31 and mcp 2.3.0.

Pins

  • agent-env and the protocol package take mcp>=1.25,<3, and the protocol's fastmcp extra <5.
  • The gateway's image keeps its own mcp<2, since it serves on mcp 1's FastMCP. That code is container-only and unchanged.
  • uv.lock stays on mcp 1: the openenv extra's fastmcp 3 needs it, until OpenEnv moves to FastMCP 4.
  • agent-env no longer declares mcp itself. Nothing outside the gateway imports it directly any more, so test_runtime_dependencies_are_imported flagged it; it arrives with agentenv-framework-protocol[computer].

agentenv_protocol.mcp_compat holds what differs between the versions:

  • server(name, **settings) builds mcp 1's FastMCP, or mcp 2's MCPServer holding the serving settings (path, host, port, transport security) that mcp 2 takes at streamable_http_app() and run() instead.
  • client_session(url, headers=, timeout=) opens an initialized ClientSession. mcp 2's client takes an httpx2 client and yields two streams, where mcp 1's took httpx and yielded three.
  • low_level_server(...) registers a low-level server's handlers: by decorator on mcp 1, by constructor on mcp 2.
  • field(result, "isError") reads an mcp type's field. mcp 2 spells the attributes in snake case only.
  • with_crash_text(error) restores a tool crash's text on mcp 2. mcp 2 reports "Error executing tool x" alone and keeps the exception's text on the server; mcp 1 sends "Error executing tool x: ". An env's errors now read the same on either version, and the crash is still logged on the server.

Where it's used

  • Protocol:
    • create_fastmcp_app and serve();
    • the per-role tools/list filter, which on mcp 2 runs in MCPServer's middleware, since mcp 2 has no request context outside a handler;
    • the peer-agents server;
    • the computer conformance probe and its client.
  • agent-env:
    • the env provider's tool listing;
    • the legacy reset;
    • the two MCP validators;
    • the two example verifiers. env_outcome_verifier runs a bundle's verify.py in agent-env's process, so verifiers see the trainer's mcp. The README tells verifier authors to use client_session and field.

Tests

  • The protocol tests run on both versions. 243 of them were module-skipped on mcp 2 by importorskip("mcp.server.fastmcp").
  • tests/mcp_versions.py (and tst/util/mcp_versions.py) give an in-memory or HTTP session whose results read by mcp 1's attribute names on either version.
  • Two tests that read the session id from mcp 1's third stream now read it from the mcp-session-id response header, or speak JSON-RPC directly.

CI: unit-mcp2 runs the unit tier on mcp 2.3 and fastmcp 4. It installs every extra but openenv, plus the dev test tools read from pyproject.toml.

  • Two passes. Importing fastmcp 4 adds mcp 1's camel-case attributes to mcp's types process-wide (fastmcp/_compat.py). In one process that hides exactly the reads this job exists to catch: run alone, 17 conformance tests failed that passed in the full suite. So the tests that import fastmcp run in a second pass.
  • Left out of both passes:
    • the gateway's tests, and the ones that build a card with the real gateway server, which serves on mcp 1 inside its image;
    • the openenv tests;
    • the two packaging tests that read every extra's installed metadata, openenv's included. The unit job runs them.

Not in this PR: porting the gateway itself to MCPServer. Its container keeps mcp 1, and trainers don't need it.

How it was tested

  • mcp 1: pytest tst/unit packages/agentenv-protocol/tests -n 10: 10,949 passed, 1 failed.
    • The failure is submit_runs_test::test_a_first_ctrl_c_lets_the_runner_finish_stopping, a Ctrl-C timing test that passes 3 of 3 alone and doesn't touch mcp. The run before passed all 10,950.
  • mcp 2.3 + fastmcp 4.1.4, both unit-mcp2 passes exactly as the workflow runs them: 9,861 passed, then 56 passed.
  • mcp 2.3 without fastmcp, where nothing can lend camel-case attributes: the protocol tests and the touched agent-env tests pass (871), with only the fastmcp-only modules skipped.
  • Local Docker on mcp 2: tst/integration/env/envs/server_env_local_test.py, which drives the Episode API, passes 5 of 5.
  • Resolution: uv pip compile of agent-env with trl[vllm] and vllm>=0.30 for x86_64 Linux and Python 3.12 resolves to TRL 1.16.0, vLLM 0.31.0 and mcp 2.3.0. On 0.9.1367 it has no solution.
  • ruff check and uv lock --check pass.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 4/5

Fix the duplicate settings arguments before merging so existing SDK callers can still build their environment apps.

Fix All in CursorFindings

  1. P1 Server settings stop startup ▶
Fix with agent prompt
### Issue 1
packages/agentenv-protocol/src/agentenv_protocol/agent_env_environment.py:467-468
`create_fastmcp_app()` now raises `TypeError` when callers supply `host` or `port` through `fastmcp_kwargs`, because those keywords are also passed explicitly. Previously, it accepted those settings and then replaced them with `MCP_HOST` and `MCP_PORT`.

On mcp 2, supplying `transport_security` also fails because `deployed_server()` passes it twice. Merge the settings before calling these helpers so existing arguments do not stop the app from starting.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds shared helpers so the protocol SDK and framework clients can run on mcp 1 or mcp 2.

  • Agent-env environments can serve tools on mcp 1 or mcp 2.
  • Agent-env MCP clients and result readers work with either mcp version.
  • The project tests mcp 2 separately while keeping its main lock on mcp 1.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  App[Environment app] --> Compat[mcp_compat]
  Clients[Framework clients] --> Compat
  Compat --> MCP1[mcp 1]
  Compat --> MCP2[mcp 2]
  Gateway[Gateway image] --> MCP1
Loading

Reviews (3) · Last reviewed commit: "ci(mcp): find the unit-mcp2 job's left-o..." · Reviewed by Greptile

… and later

vLLM 0.30 and later require mcp>=2, and agent-env and its protocol package
pinned mcp<2, so a trainer with agent-env installed was held to vLLM 0.29.

- The pins widen to mcp>=1.25,<3 (fastmcp <5). The gateway's image keeps
  its own mcp<2, since it serves on mcp 1's FastMCP. uv.lock stays on mcp 1:
  the openenv extra's fastmcp 3 needs it.
- agentenv_protocol.mcp_compat holds what differs: the high-level server
  (FastMCP, or MCPServer holding mcp 1's serving settings), a client session
  (httpx2 and two streams on mcp 2), the low-level server's handlers, an mcp
  type's field under either spelling, and a crash's text in a tool's error,
  which mcp 2 keeps on the server.
- The protocol's FastMCP app, its tools/list filter per role (middleware on
  mcp 2), the peer-agents server and the computer conformance probe use it,
  and so do agent-env's MCP clients and the example verifiers. agent-env no
  longer declares mcp itself; it arrives with the protocol package.
- The tests run on both versions, and a unit-mcp2 job runs the unit tier on
  mcp 2.3 and fastmcp 4, in two passes, since fastmcp 4 adds mcp 1's
  camel-case attributes to mcp's types once imported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/agentenv-protocol/src/agentenv_protocol/mcp_compat.py Outdated
earakely-scale and others added 3 commits October 27, 2026 15:28
…mcp2's venv on PATH

client_session built a plain httpx client when no timeout was given, so the
env provider's tool listing, the legacy reset and the example verifiers lost
mcp's 30 s and 300 s timeouts for httpx's 5 s default. It now builds the
client with mcp's own create_mcp_http_client on both versions, and a test
checks the defaults and an explicit timeout.

The generated-CLI tests run their script on python3 from PATH, which the
unit-mcp2 job didn't point at its venv, as the unit job does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…they import

A test added on main that imports the gateway server broke the job's fixed
--ignore list. .github/scripts/mcp2_test_split.py now finds the tests that
import the gateway server, directly or through a test module that does, and
the protocol tests that import fastmcp, so a new one is placed on its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment on lines +467 to +468
app = mcp_compat.deployed_server(card.name, host=os.environ.get("MCP_HOST", "0.0.0.0"),
port=int(os.environ.get("MCP_PORT", "18765")), **fastmcp_kwargs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Server settings stop startup

create_fastmcp_app() now raises TypeError when callers supply host or port through fastmcp_kwargs, because those keywords are also passed explicitly. Previously, it accepted those settings and then replaced them with MCP_HOST and MCP_PORT.

On mcp 2, supplying transport_security also fails because deployed_server() passes it twice. Merge the settings before calling these helpers so existing arguments do not stop the app from starting.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/agentenv-protocol/src/agentenv_protocol/agent_env_environment.py
Line: 467-468

Comment:
**Server settings stop startup**

`create_fastmcp_app()` now raises `TypeError` when callers supply `host` or `port` through `fastmcp_kwargs`, because those keywords are also passed explicitly. Previously, it accepted those settings and then replaced them with `MCP_HOST` and `MCP_PORT`.

On mcp 2, supplying `transport_security` also fails because `deployed_server()` passes it twice. Merge the settings before calling these helpers so existing arguments do not stop the app from starting.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Cursor Fix in Claude Code Fix in Codex

@earakely-scale
earakely-scale marked this pull request as draft October 28, 2026 00:42
@earakely-scale

Copy link
Copy Markdown
Collaborator Author

Parked as a draft until OpenEnv ships FastMCP 4 (openenv/OpenEnv#1304).

The TRL route through OpenEnv needs OpenEnv in the trainer, and OpenEnv 0.10 requires mcp 1. So this PR doesn't unblock vLLM 0.30+ for that route yet, and the first TRL runs can use vLLM 0.29. Merging now would mean carrying the compat layer and unit-mcp2 meanwhile, and our agent and env images would move to mcp 2 at their next build.

Once #1304 is released, we'll decide between this dual support and mcp 2 only, with the OpenEnv check done in the same change. The branch is kept: CI was green apart from integration-local-slow, which is also timing out on main.

🤖 Generated with Claude Code

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant