Skip to content

nhynes/openenv-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

openenv

Rust server scaffolding for OpenEnv-compatible environments.

This crate is intentionally independent of any simulation engine. An environment implements Environment; the crate supplies the OpenEnv-compatible HTTP, WebSocket, schema, OpenAPI, session lifecycle, and MCP protocol surfaces.

Compatibility

The server mirrors the Python OpenEnv server shape:

  • GET /health
  • GET /metadata
  • GET /schema
  • GET /openapi.json
  • WS /ws for persistent reset, step, state, close, and embedded mcp messages
  • POST /mcp for Python OpenEnv-compatible JSON-RPC MCP requests
  • WS /mcp for persistent MCP JSON-RPC sessions
  • production mode omits direct HTTP /reset, /step, and /state
  • simulation mode exposes stateless HTTP /reset, /step, and /state
  • openenv/session/create and openenv/session/close for HTTP MCP sessions
  • tools/list and tools/call, using RMCP CallToolResult wire shape

MCP tool names reset, step, state, and close are reserved, matching Python OpenEnv's separation between orchestration controls and agent tools.

Example

use async_trait::async_trait;
use openenv::{Environment, OpenEnvServer, StepResult};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Default, Serialize, Deserialize, JsonSchema)]
struct Reset;

#[derive(Serialize, Deserialize, JsonSchema)]
struct Action {
    delta: i64,
}

#[derive(Serialize, JsonSchema)]
struct Observation {
    count: i64,
}

#[derive(Serialize, JsonSchema)]
struct State {
    count: i64,
}

struct Counter {
    count: i64,
}

#[async_trait]
impl Environment for Counter {
    type Reset = Reset;
    type Action = Action;
    type Observation = Observation;
    type State = State;

    async fn reset(&mut self, _: Reset) -> openenv::Result<StepResult<Observation>> {
        self.count = 0;
        Ok(StepResult::new(Observation { count: self.count }))
    }

    async fn step(&mut self, action: Action) -> openenv::Result<StepResult<Observation>> {
        self.count += action.delta;
        Ok(StepResult::new(Observation { count: self.count }))
    }

    async fn state(&mut self) -> openenv::Result<State> {
        Ok(State { count: self.count })
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let app = OpenEnvServer::new(|| Counter { count: 0 })
        .production()
        .router();
    let listener = tokio::net::TcpListener::bind("127.0.0.1:8000").await?;
    axum::serve(listener, app).await?;
    Ok(())
}

About

OpenEnv scaffolding for Rust

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages