> This page is for version 0.5.1.
> For other versions, use one of these documentation indexes:
> - Main (default): https://docs.nvidia.com/nemo/gym/main/llms.txt
> - 0.7.0: https://docs.nvidia.com/nemo/gym/v0.7.0/llms.txt
> - 0.6.0: https://docs.nvidia.com/nemo/gym/v0.6.0/llms.txt
> - 0.5.1: https://docs.nvidia.com/nemo/gym/v0.5.1/llms.txt
> - 0.5.0: https://docs.nvidia.com/nemo/gym/v0.5.0/llms.txt
> - 0.4.0: https://docs.nvidia.com/nemo/gym/v0.4.0/llms.txt
> - 0.3.0: https://docs.nvidia.com/nemo/gym/v0.3.0/llms.txt
> - 0.2.1: https://docs.nvidia.com/nemo/gym/v0.2.1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/gym/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/gym/_mcp/server.

# nemo_gym.server_runtime.base_resources_server

## Module Contents

### Classes

| Name                                                                                                            | Description                                                                                   |
| --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| [`BaseMultiRewardVerifyResponse`](#nemo_gym-server_runtime-base_resources_server-BaseMultiRewardVerifyResponse) | Base verify response for environments with multiple reward objectives.                        |
| [`BaseResourcesServer`](#nemo_gym-server_runtime-base_resources_server-BaseResourcesServer)                     | -                                                                                             |
| [`BaseResourcesServerConfig`](#nemo_gym-server_runtime-base_resources_server-BaseResourcesServerConfig)         | -                                                                                             |
| [`BaseRunRequest`](#nemo_gym-server_runtime-base_resources_server-BaseRunRequest)                               | -                                                                                             |
| [`BaseSeedSessionRequest`](#nemo_gym-server_runtime-base_resources_server-BaseSeedSessionRequest)               | -                                                                                             |
| [`BaseSeedSessionResponse`](#nemo_gym-server_runtime-base_resources_server-BaseSeedSessionResponse)             | -                                                                                             |
| [`BaseVerifyRequest`](#nemo_gym-server_runtime-base_resources_server-BaseVerifyRequest)                         | -                                                                                             |
| [`BaseVerifyResponse`](#nemo_gym-server_runtime-base_resources_server-BaseVerifyResponse)                       | -                                                                                             |
| [`LostSessionVerification`](#nemo_gym-server_runtime-base_resources_server-LostSessionVerification)             | The verify response fields of a masked sample for a session the server no longer holds.       |
| [`MCPServerMetadata`](#nemo_gym-server_runtime-base_resources_server-MCPServerMetadata)                         | Metadata returned from /seed_session for per-rollout Gym MCP access.                          |
| [`ResourcesCloseSessionRequest`](#nemo_gym-server_runtime-base_resources_server-ResourcesCloseSessionRequest)   | Close resources-server state.                                                                 |
| [`ResourcesCloseSessionResponse`](#nemo_gym-server_runtime-base_resources_server-ResourcesCloseSessionResponse) | Confirm resources-server state was closed.                                                    |
| [`ResourcesSeedRejectedError`](#nemo_gym-server_runtime-base_resources_server-ResourcesSeedRejectedError)       | Refuse a seed whose input no retry can fix, such as `task_data` that fails validation.        |
| [`ResourcesSeedSessionRequest`](#nemo_gym-server_runtime-base_resources_server-ResourcesSeedSessionRequest)     | Idempotently initialize resources state under a caller-assigned identifier.                   |
| [`ResourcesSeedSessionResponse`](#nemo_gym-server_runtime-base_resources_server-ResourcesSeedSessionResponse)   | Confirm resources state and return optional agent access and an initial observation.          |
| [`ResourcesSessionConflictError`](#nemo_gym-server_runtime-base_resources_server-ResourcesSessionConflictError) | Refuse a seed or close that conflicts with what this server recorded for the session.         |
| [`ResourcesSessionFences`](#nemo_gym-server_runtime-base_resources_server-ResourcesSessionFences)               | The fences of the resources sessions this worker closed, each lasting the close retry window. |
| [`ResourcesSessionLocks`](#nemo_gym-server_runtime-base_resources_server-ResourcesSessionLocks)                 | Lock each resources session so that its seed and close never interleave.                      |
| [`ResourcesSessionLostError`](#nemo_gym-server_runtime-base_resources_server-ResourcesSessionLostError)         | Answer a tool call for a session this server no longer holds with HTTP 404.                   |
| [`ResourcesVerifyRequest`](#nemo_gym-server_runtime-base_resources_server-ResourcesVerifyRequest)               | Carry typed environment output to a resources server.                                         |
| [`ReverifyMode`](#nemo_gym-server_runtime-base_resources_server-ReverifyMode)                                   | -                                                                                             |
| [`SimpleResourcesServer`](#nemo_gym-server_runtime-base_resources_server-SimpleResourcesServer)                 | -                                                                                             |
| [`_SessionLock`](#nemo_gym-server_runtime-base_resources_server-_SessionLock)                                   | -                                                                                             |

### Functions

| Name                                                                                                          | Description                                                                                 |
| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| [`lost_session_verification`](#nemo_gym-server_runtime-base_resources_server-lost_session_verification)       | Return the verify response fields for an episode whose session this server no longer holds. |
| [`mcp_session_token_payload`](#nemo_gym-server_runtime-base_resources_server-mcp_session_token_payload)       | The payload of a Gym MCP session token whose signature verifies, or `None` if it does not.  |
| [`mcp_session_token_serializer`](#nemo_gym-server_runtime-base_resources_server-mcp_session_token_serializer) | The signer of Gym MCP session tokens; build it once per server.                             |
| [`normalize_tool_name`](#nemo_gym-server_runtime-base_resources_server-normalize_tool_name)                   | Map a trajectory tool-call name to the server's bare tool name.                             |

### Data

[`FenceRecordT`](#nemo_gym-server_runtime-base_resources_server-FenceRecordT)

[`MCP_TOKEN_EPISODE_CLAIM`](#nemo_gym-server_runtime-base_resources_server-MCP_TOKEN_EPISODE_CLAIM)

[`NEMO_GYM_MCP_METADATA_KEY`](#nemo_gym-server_runtime-base_resources_server-NEMO_GYM_MCP_METADATA_KEY)

[`NEMO_GYM_MCP_SESSION_TOKEN_HEADER`](#nemo_gym-server_runtime-base_resources_server-NEMO_GYM_MCP_SESSION_TOKEN_HEADER)

[`RESERVED_MCP_TOOL_NAMES`](#nemo_gym-server_runtime-base_resources_server-RESERVED_MCP_TOOL_NAMES)

[`RESOURCES_SESSIONS`](#nemo_gym-server_runtime-base_resources_server-RESOURCES_SESSIONS)

[`VerificationInputT`](#nemo_gym-server_runtime-base_resources_server-VerificationInputT)

[`_MCP_TOKEN_SALT`](#nemo_gym-server_runtime-base_resources_server-_MCP_TOKEN_SALT)

[`_SESSION_HOOKS`](#nemo_gym-server_runtime-base_resources_server-_SESSION_HOOKS)

[`logger`](#nemo_gym-server_runtime-base_resources_server-logger)

### API

```python
class nemo_gym.server_runtime.base_resources_server.BaseMultiRewardVerifyResponse()
```

**Bases:** [BaseVerifyResponse](#nemo_gym-server_runtime-base_resources_server-BaseVerifyResponse)

Base verify response for environments with multiple reward objectives.

Subclass this response instead of declaring `reward_components` on an
environment-specific `BaseVerifyResponse` subclass. The mapping is required, and
its objective keys should remain consistent across every task in the environment.

Set the inherited `reward` to the scalar aggregate expected by single-reward
consumers. To include individual objectives in aggregate metrics, also expose them
as top-level numeric fields because metrics do not descend into this mapping. See
`resources_servers/example_tool_call_multireward` for a complete example.

**`reward_components`** `dict[str, float]`

---

```python
class nemo_gym.server_runtime.base_resources_server.BaseResourcesServer()
```

**Bases:** [BaseServer](/nemo/gym/nemo-gym/nemo_gym/server_runtime/base_server#nemo_gym-server_runtime-base_server-BaseServer)

**`config`** `BaseResourcesServerConfig`

---

```python
class nemo_gym.server_runtime.base_resources_server.BaseResourcesServerConfig()
```

**Bases:** [BaseRunServerInstanceConfig](/nemo/gym/nemo-gym/nemo_gym/config/config_types#nemo_gym-config-config_types-BaseRunServerInstanceConfig)

**`REVERIFY_MODE`** `ReverifyMode = ReverifyMode.UNKNOWN`

---

**`expose_tools_over_mcp`** `bool = False`

---

**`session_close_retry_window_seconds`** `float`

---

```python
class nemo_gym.server_runtime.base_resources_server.BaseRunRequest()
```

**Bases:** `BaseModel`

**`capture_rollout_id`** `Optional[str]`

---

**`model_config`** `= ConfigDict(populate_by_name=True)`

---

**`responses_create_params`** `NeMoGymResponseCreateParamsNonStreaming`

---

```python
class nemo_gym.server_runtime.base_resources_server.BaseSeedSessionRequest()
```

**Bases:** `BaseModel`

```python
class nemo_gym.server_runtime.base_resources_server.BaseSeedSessionResponse()
```

**Bases:** `BaseModel`

```python
class nemo_gym.server_runtime.base_resources_server.BaseVerifyRequest()
```

**Bases:** [BaseRunRequest](#nemo_gym-server_runtime-base_resources_server-BaseRunRequest)

**`response`** `NeMoGymResponse`

---

```python
class nemo_gym.server_runtime.base_resources_server.BaseVerifyResponse()
```

**Bases:** [BaseVerifyRequest](#nemo_gym-server_runtime-base_resources_server-BaseVerifyRequest)

**`failure_kind`** `Optional[str]`

---

**`failure_outcome_action`** `OutcomeAction | None`

---

**`failure_outcome_attribution`** `FailureClass | None`

---

**`failure_reason`** `Optional[str] = None`

---

**`mask_sample`** `bool`

---

**`ng_verifier_invocations`** `list[VerifierInvocation]`

---

**`reward`** `float`

---

```python
nemo_gym.server_runtime.base_resources_server.BaseVerifyResponse._warn_on_unregistered_failure_kind(
    value: typing.Optional[str]
) -> typing.Optional[str]
```

classmethod

Producers learn about a name outside the vocabulary without losing the failure.

Validation warns rather than rejects: an unregistered kind is a migration signal,
and dropping the response over it would replace a visible wrong label with an
invisible lost failure.

```python
class nemo_gym.server_runtime.base_resources_server.LostSessionVerification
```

**Bases:** `typing.TypedDict`

The verify response fields of a masked sample for a session the server no longer holds.

**`failure_kind`** `str`

---

**`failure_reason`** `str`

---

**`mask_sample`** `bool`

---

**`reward`** `float`

---

```python
class nemo_gym.server_runtime.base_resources_server.MCPServerMetadata()
```

**Bases:** `BaseModel`

Metadata returned from /seed_session for per-rollout Gym MCP access.

**`headers`** `dict[str, str]`

---

**`server_name`** `str`

---

**`transport`** `str = 'http'`

---

**`url_path`** `str = '/mcp'`

---

```python
class nemo_gym.server_runtime.base_resources_server.ResourcesCloseSessionRequest()
```

**Bases:** `BaseModel`

Close resources-server state.

**`episode_id`** `EpisodeId`

---

**`model_config`** `= ConfigDict(extra='forbid')`

---

**`resources_session_id`** `str`

---

```python
class nemo_gym.server_runtime.base_resources_server.ResourcesCloseSessionResponse()
```

**Bases:** `BaseModel`

Confirm resources-server state was closed.

**`model_config`** `= ConfigDict(extra='forbid')`

---

**`resources_session_id`** `str`

---

```python
class nemo_gym.server_runtime.base_resources_server.ResourcesSeedRejectedError(
    detail: str
)
```

Exception

**Bases:** `HTTPException`, `ValueError`

Refuse a seed whose input no retry can fix, such as `task_data` that fails validation.

FastAPI answers it with HTTP 422, which an Environment Server treats as terminal, so the episode fails once
with this message instead of being dispatched again with the same input. It is also a `ValueError`, so code
that seeds outside a request (a direct call in a test) keeps catching it as before.

```python
class nemo_gym.server_runtime.base_resources_server.ResourcesSeedSessionRequest()
```

**Bases:** `BaseModel`

Idempotently initialize resources state under a caller-assigned identifier.

Repeating the same identifier and episode must return the existing session.
Closing an unknown identifier must prevent a racing seed from creating it
for session_close_retry_window_seconds (see ResourcesSessionFences);
callers must use unique IDs and finish seed retries within that window.
External resources should use provider TTLs when available. Current process-local
session records may remain until the server shuts down.

**`episode_id`** `EpisodeId`

---

**`model_config`** `= ConfigDict(extra='forbid')`

---

**`resources_session_id`** `str = Field(min_length=1)`

---

**`task_data`** `dict[str, JsonValue]`

---

**`task_id`** `TaskId`

---

```python
class nemo_gym.server_runtime.base_resources_server.ResourcesSeedSessionResponse()
```

**Bases:** `BaseModel`

Confirm resources state and return optional agent access and an initial observation.

**`model_config`** `= ConfigDict(extra='forbid')`

---

**`observation`** `str | None = None`

---

**`resources_session_id`** `str`

---

**`resources_tools`** `MCPServerMetadata | None = None`

---

**`sandbox_access`** `SandboxAccess | None = None`

---

```python
nemo_gym.server_runtime.base_resources_server.ResourcesSeedSessionResponse._serialize_observation(
    handler: pydantic.SerializerFunctionWrapHandler
)
```

Preserve the seed wire shape when the server supplies no observation field.

```python
class nemo_gym.server_runtime.base_resources_server.ResourcesSessionConflictError(
    detail: str
)
```

Exception

**Bases:** `HTTPException`, `ValueError`

Refuse a seed or close that conflicts with what this server recorded for the session.

Examples: a `resources_session_id` already bound to another episode or task, a close that names another
episode, or a seed for a session that already closed. FastAPI answers it with HTTP 409, which an Environment
Server treats as terminal. It is also a `ValueError` for direct callers.

```python
class nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences(
    close_retry_window_seconds: float
)
```

**Bases:** `Generic[FenceRecordT]`

The fences of the resources sessions this worker closed, each lasting the close retry window.

A fence is the record a close leaves so that a seed arriving after the close (a retry, or a reply that raced
the close) is refused instead of creating the session again. Each fence holds a record of the server's choice,
such as the episode that first closed the session, which a retried close can check.
A fence expires `close_retry_window_seconds` after the first close; a retried close does not extend it.
Construct it with the server's `session_close_retry_window_seconds`.

With several workers, the worker's ownership of the ID expires that long after the last request for the session,
and the fence is set before the close's reply is sent.
So ownership never expires before the fence
(see `nemo_gym.server_runtime.request_handling.session_routing.ResourcesSessionLifetimes`).
While ownership lasts, a late seed reaches the worker that holds the fence and is refused.
After it expires, every worker treats the ID as new, so the answer does not depend on which worker gets it.

**`_fences`** `OrderedDict[str, tuple[float, FenceRecordT]] = OrderedDict()`

---

```python
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.__contains__(
    resources_session_id: object
) -> bool
```

```python
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.__len__() -> int
```

```python
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences._expire() -> None
```

```python
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.clear() -> None
```

Delete every fence, for a server that drops all of its sessions at shutdown.

```python
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.fence(
    resources_session_id: str,
    record: nemo_gym.server_runtime.base_resources_server.FenceRecordT
) -> None
```

Fence a closed session; call it only after its state was released.

A retried close keeps the first close's record and expiry.

```python
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.get(
    resources_session_id: str
) -> typing.Optional[nemo_gym.server_runtime.base_resources_server.FenceRecordT]
```

Return the record of the session's fence, or None if it is not fenced or its fence expired.

A fence may hold a None record, so test `resources_session_id in fences` to ask whether a session is fenced.

```python
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.raise_if_fenced(
    resources_session_id: str
) -> None
```

Refuse a seed for a fenced session with 409, which the environment server treats as terminal.

```python
class nemo_gym.server_runtime.base_resources_server.ResourcesSessionLocks()
```

Lock each resources session so that its seed and close never interleave.

A session's lock exists only while a request holds or waits for it.

**`_locks`** `dict[str, _SessionLock] = {}`

---

```python
nemo_gym.server_runtime.base_resources_server.ResourcesSessionLocks.locked(
    resources_session_id: str
) -> collections.abc.AsyncIterator[None]
```

async

Hold the lock for `resources_session_id`.

```python
class nemo_gym.server_runtime.base_resources_server.ResourcesSessionLostError(
    message: str,
    failure_kind: str = SESSION_LOST
)
```

Exception

**Bases:** `HTTPException`

Answer a tool call for a session this server no longer holds with HTTP 404.

Raise this instead of returning an ordinary tool observation that says the session is gone. What a harness does
with the 404 varies: simple_agent returns its body to the model as the tool output and keeps going, Stirrup
fails the run so the episode is dispatched again, and an MCP client reports a tool error. In every case the
trajectory records a failed tool call whose detail names the `failure_kind`, which a log can group by. The
verify for the session answers with `lost_session_verification`, whose masked sample keeps the episode out
of the score.

```python
class nemo_gym.server_runtime.base_resources_server.ResourcesVerifyRequest()
```

**Bases:** `BaseModel`, `Generic[VerificationInputT]`

Carry typed environment output to a resources server.

**`episode_id`** `EpisodeId`

---

**`model_config`** `= ConfigDict(extra='forbid')`

---

**`task_id`** `TaskId`

---

**`verification_input`** `VerificationInputT`

---

```python
class nemo_gym.server_runtime.base_resources_server.ReverifyMode
```

**Bases:** `enum.Enum`

**`STATELESS`** `= 'stateless'`

---

**`UNKNOWN`** `= 'unknown'`

---

**`UNSUPPORTED`** `= 'unsupported'`

---

```python
class nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer()
```

**Bases:** [BaseResourcesServer](#nemo_gym-server_runtime-base_resources_server-BaseResourcesServer), [AggregateMetricsMixin](/nemo/gym/nemo-gym/nemo_gym/metrics/reward_profile#nemo_gym-metrics-reward_profile-AggregateMetricsMixin), [SimpleServer](/nemo/gym/nemo-gym/nemo_gym/server_runtime/base_server#nemo_gym-server_runtime-base_server-SimpleServer)

**`_checkpoint`** `Optional[ResourcesParticipant] = PrivateAttr(default=None)`

---

**`_mcp_token_serializer`** `Any = PrivateAttr(default=None)`

---

**`checkpoint_mode`** `CheckpointMode = 'restart_only'`

---

**`checkpoint_participant`** `Optional[ResourcesParticipant]`

The participant that takes part in partial-rollout checkpoints, or `None` with checkpointing off.

---

**`checkpoint_session_starts`** `tuple[str, ...] = ('/seed_session',)`

---

**`checkpoint_verify`** `StepType = 'wait'`

---

**`config`** `BaseResourcesServerConfig`

---

**`grading_may_use_uninstrumented_model`** `bool = False`

---

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer._mcp_token_claims(
    scope: dict[str, typing.Any]
) -> typing.Optional[nemo_gym.server_runtime._checkpoint.resources.MCPTokenClaims]
```

The session and attempt an MCP request's signed token names.

`None` without a token or for one that does not verify; the MCP endpoint rejects a bad token.
An attempt claim that does not decode counts as missing.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.aggregate_metrics(
    body: nemo_gym.config.config_types.AggregateMetricsRequest
) -> nemo_gym.config.config_types.AggregateMetrics
```

async

Compute aggregate metrics from verify responses.

RewardProfiler provides baseline stats. Override compute_metrics(),
compute_repeat_metrics(), and/or get_key_metrics() for benchmark-specific customization.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.checkpoint_session_ended(
    request: fastapi.Request
) -> None
```

Report that the request's session ended; standard `/close_session` and `/verify` do this.

Call it from a protocol route that ends a session under another name, such as a terminal step.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.close_resources_session(
    body: nemo_gym.server_runtime.base_resources_server.ResourcesCloseSessionRequest
) -> nemo_gym.server_runtime.base_resources_server.ResourcesCloseSessionResponse
```

async

Close state an Environment Server seeded; the default keeps none.

Served at `/close_session`. Servers that keep per-rollout state override this method, and may accept
other close bodies by overriding it with a different signature. The name differs from the
`close_session` helpers some servers already define for their own state.

An override must:

* succeed when the session is already closed or was never seeded;
* keep a seed that arrives after the close from creating the session again, for a bounded time
  (`ResourcesSessionFences` does this for `session_close_retry_window_seconds`);
* release the session's state by `resources_session_id` even when the request carries no session cookie;
* raise when releasing fails rather than recording the session as closed, so the caller can retry.

A served resources server routes sessions (see `nemo_gym.server_runtime.request_handling.session_routing`):
an episode seed's session cookie carries `resources_session_id` as its `SESSION_ID_KEY`, and an episode
close that carries no cookie, as when the seed's reply was lost, is given that ID too, so a server may key
state by either; they name the same session. An app built without session routing, such as one under `TestClient`, gives a new session a random
ID instead, so release by `body.resources_session_id` to be correct either way.

`nemo_gym.server_runtime.testing.session_conformance.check_resources_session_contract` checks these rules from a server's tests.

TODO: also expire sessions that are never closed, as when the environment server dies before its close. Planned
rule: a fixed lifetime from the seed (the sandbox `ttl_s` when set, else 24 hours), checked on the next
request and at shutdown; an expired session is released, fenced, and verifies as `SESSION_EXPIRED`.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.create_resources_app() -> fastapi.FastAPI
```

Build the app with what every resources server shares, whatever its episode protocol.

That is the session and rollout middleware, the checkpoint participant,
`/aggregate_metrics` and `/reverify_mode`.
A server with another protocol, such as Gymnasium's `/reset` and `/step`,
overrides `register_protocol_routes` instead of building its own app.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.export_session_states(
    session_ids: list[str]
) -> dict[str, pydantic.JsonValue]
```

async

Return each session's state for a checkpoint, leaving out sessions this server no longer holds.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.get_reverify_mode() -> nemo_gym.server_runtime.base_resources_server.ReverifyMode
```

async

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.mcp_allowed_tools_for_session(
    seed_body: dict[str, typing.Any]
) -> typing.Optional[list[str]]
```

Per-session tool restriction: return the tool names allowed for this rollout's MCP token,
or `None` (the default) for unrestricted. `seed_body` is the JSON body POSTed to
`/seed_session`, or its `task_data` when an Environment Server seeds the session.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.mcp_tools(
    harvested: list[nemo_gym.server_runtime.mcp_auto_exposure.MCPTool],
    catchall: typing.Optional[typing.Any]
) -> typing.Optional[list[nemo_gym.server_runtime.mcp_auto_exposure.MCPTool]]
```

Return the MCP tools to expose (default: the auto-harvested typed POST routes).

Override to exclude (filter harvested), add catch-all-backed tools (harvested + \[catchall.tool(...)]),
or disable (return None). 'catchall' is None unless the server has one parameterized catch-all route.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.normalize_tool_name(
    name: str
) -> str
```

Strip this server's MCP namespace from a trajectory tool-call name (see module function).

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.owned_session_routing() -> dict[str, typing.Any]
```

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.register_protocol_routes(
    app: fastapi.FastAPI
) -> None
```

Register the episode routes `/seed_session`, `/close_session` and `/verify`.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.restore_session_states(
    states: dict[str, pydantic.JsonValue]
) -> None
```

async

Validate every state, then install all of them; never install a partial set.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.retire_session_state(
    session_id: str
) -> None
```

async

Discard a session whose attempt was retired; called in every checkpoint mode.

The default does nothing.
A server that keeps per-session state releases it here, since a retired session's late requests,
a terminal step among them, are refused.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.seed_session(
    body: nemo_gym.server_runtime.base_resources_server.ResourcesSeedSessionRequest | nemo_gym.server_runtime.base_resources_server.BaseSeedSessionRequest
) -> nemo_gym.server_runtime.base_resources_server.ResourcesSeedSessionResponse | nemo_gym.server_runtime.base_resources_server.BaseSeedSessionResponse
```

async

Seed per-rollout state; the default keeps none.

An Environment Server seeds with a `ResourcesSeedSessionRequest` and gets its caller-assigned
`resources_session_id` back. An Agent's `/run` seeds with its legacy body and gets an empty
response. Servers that keep per-rollout state override this method.

An override refuses a seed that no retry can make succeed with a terminal 4xx, never a plain exception
(HTTP 500), which the Environment Server retries by dispatching the whole episode again:

* `ResourcesSeedRejectedError` (422) for input that fails validation, such as `task_data` or a
  `TaskId` that does not match the task row;
* `ResourcesSessionConflictError` (409) for an ID bound to another episode or task, or a closed session.

A failure that a retry may fix, such as an unavailable sandbox provider, stays a 5xx.

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.setup_checkpoint_participant(
    app: fastapi.FastAPI
) -> None
```

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.setup_resources_checkpoint(
    app: fastapi.FastAPI
) -> None
```

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.setup_webserver() -> fastapi.FastAPI
```

```python
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.verify(
    body: nemo_gym.server_runtime.base_resources_server.BaseVerifyRequest
) -> nemo_gym.server_runtime.base_resources_server.BaseVerifyResponse
```

async

abstract

Score the episode.

A server whose score depends on per-rollout state answers a verify for a session it no longer holds
(never seeded, closed, or lost) with a masked sample built by `lost_session_verification`, never a
plain reward of 0 and never an exception that dispatches the episode again. A tool call for such a
session raises `ResourcesSessionLostError` (404).

```python
class nemo_gym.server_runtime.base_resources_server._SessionLock(
    lock: asyncio.Lock = asyncio.Lock(),
    users: int = 0
)
```

Dataclass

**`lock`** `Lock = field(default_factory=(asyncio.Lock))`

---

**`users`** `int = 0`

---

```python
nemo_gym.server_runtime.base_resources_server.lost_session_verification(
    reason: str,
    failure_kind: str = SESSION_LOST
) -> nemo_gym.server_runtime.base_resources_server.LostSessionVerification
```

Return the verify response fields for an episode whose session this server no longer holds.

The sample is masked: the environment lost the state it would measure, so a reward of 0 would read as a
policy that solved nothing. Merge the result over the request's fields so these win, for example
`BaseVerifyResponse(**(body.model_dump() | lost_session_verification("sandbox gone")))`; spreading both as
keyword arguments raises `TypeError` when a request model that allows extra fields already carries a
`reward`. Use `failure_kind=SESSION_EXPIRED` when the server closed the session itself because its lifetime
ran out.

Each call logs a warning, because a masked sample is otherwise visible only in coverage metrics: many of them
point at a wiring fault such as a session cookie that never reaches `/verify`.

```python
nemo_gym.server_runtime.base_resources_server.mcp_session_token_payload(
    serializer: itsdangerous.URLSafeSerializer,
    token: str
) -> typing.Any
```

The payload of a Gym MCP session token whose signature verifies, or `None` if it does not.

```python
nemo_gym.server_runtime.base_resources_server.mcp_session_token_serializer(
    secret: str
) -> itsdangerous.URLSafeSerializer
```

The signer of Gym MCP session tokens; build it once per server.

```python
nemo_gym.server_runtime.base_resources_server.normalize_tool_name(
    name: str,
    server_name: typing.Optional[str] = None
) -> str
```

Map a trajectory tool-call name to the server's bare tool name.

HTTP-driven agents record bare tool names ("email_reply_email"); MCP-native agents (e.g.
Claude Code) record them namespaced per server ("mcp\_\_workplace_assistant\_\_email_reply_email").
Verifiers compare trajectory names against dataset/ground-truth vocabulary, so names are
normalized before verify sees them and rollouts score identically on both transports.
Non-namespaced names pass through unchanged. When `server_name` is given, only that server's
prefix is stripped (robust to tool names that themselves contain double underscores).
This runs only for servers exposed over MCP and mirrors how MCP clients namespace tool names,
so a real tool that is itself named `mcp__<server>__x` being stripped is accepted.

```python
nemo_gym.server_runtime.base_resources_server.FenceRecordT = TypeVar('FenceRecordT')
```

```python
nemo_gym.server_runtime.base_resources_server.MCP_TOKEN_EPISODE_CLAIM = 'episode'
```

```python
nemo_gym.server_runtime.base_resources_server.NEMO_GYM_MCP_METADATA_KEY = 'mcp'
```

```python
nemo_gym.server_runtime.base_resources_server.NEMO_GYM_MCP_SESSION_TOKEN_HEADER = 'X-NeMo-Gym-Session-Token'
```

```python
nemo_gym.server_runtime.base_resources_server.RESERVED_MCP_TOOL_NAMES = frozenset({'verify', 'seed_session', 'close_session', 'aggregate_metrics', 'mcp'...
```

```python
nemo_gym.server_runtime.base_resources_server.RESOURCES_SESSIONS = SessionKind(cookie_key=SESSION_ID_KEY, id_field='resources_session_id', seed_pat...
```

```python
nemo_gym.server_runtime.base_resources_server.VerificationInputT = TypeVar('VerificationInputT')
```

```python
nemo_gym.server_runtime.base_resources_server._MCP_TOKEN_SALT = 'nemo-gym-mcp-session-token'
```

```python
nemo_gym.server_runtime.base_resources_server._SESSION_HOOKS = ('export_session_states', 'restore_session_states', 'retire_session_state')
```

```python
nemo_gym.server_runtime.base_resources_server.logger = logging.getLogger(__name__)
```