Skip to content
eve-mocks
Esc
↑↓navigate↵open⌘Jpreview
On this page

Constraints

What eve-mocks does not mock, and why.

eve-mocks works by loading a preload into every process the wrapped command spawns. The preload patches global fetch and guards Node’s HTTP modules. Each limit below follows from that.

Only fetch is mocked

node:http, node:https, and node:http2 are blocked, not mocked. A client built on them, such as axios, got, or a gRPC-based SDK, throws unless its URL is allowed, and counts as block. Call the upstream with fetch to mock it. eve’s own connections all use fetch.

Raw node:net and node:tls sockets and the undici package used directly are neither mocked nor blocked.

Sandbox traffic is out of reach

Code the agent runs in an eve sandbox executes on another machine or in a container, which the preload does not reach. It is neither mocked nor blocked. What it may call is decided by the sandbox’s own network policy.

Pinned eval answers depend on two eve internals

eve gives an eval no ID, so mock(t, …) relies on two parts of eve that eve doesn’t document:

  • t.send and t.session: eve-mocks wraps both on the eval’s t to record which sessions the eval starts
  • eve’s context store: eve-mocks reads the session ID of each call from Symbol.for("eve.context-storage")

If a future eve release changes either one, pinned answers stop applying and the mock file answers instead. See Evals.

The MCP transport is stateless

This is about the protocol, not the data: a mock can keep state. eve’s MCP client calls tools without an mcp-session-id header, as the hosted servers allow. A client that checks sessions gets Missing mcp-session-id header (HTTP 400). See the transport spec.

State lives in the process that answers

A mock’s state is kept in memory by the process that handles the call, one per eve session. eve’s dev server runs a session’s steps, so a session sees one state. Code that calls the upstream from a separate process of its own starts from a fresh state() there. State is gone when the run ends.

Every top-level file in mocks/ is imported

By the CLI and by the preload, on every load. Keep scripts, fixtures, and tests in a subfolder; subfolders are ignored.

Relative imports need the .ts extension

Node loads mock files by type stripping, which does not resolve extensionless imports. For the same reason a mock file cannot import app code that uses them.

Dynamic connections built per session

list and add read a dynamic connection’s URL by importing its module with eve’s connection constructors wrapped. The session.started handler never runs, so an audience, flag, or tenant check does not matter.

// URL read at import: shown in `list`, matched to a mock by URL.
const connection = defineMcpClientConnection({ url: "https://logs.example.com/mcp" });
export default defineDynamic({ events: { "session.started": (_event, ctx) => (isAllowed(ctx) ? connection : null) } });

// URL built per session: nothing to read. Shown without a URL, matched by mock file name.
export default defineDynamic({
  events: { "session.started": (_event, ctx) => defineMcpClientConnection({ url: getTenantUrl(ctx) }) },
});

Both are blocked until mocked or allowed; deny by default does not need the URL. Reading the URL needs Node 22.15 or newer, which the CLI’s shebang provides under every package manager.

Was this page helpful?