aeat.entrypoints.mcp._call_runtime module

Supervised subprocess runtime for the MCP call path.

Every MCP tool call shells the deterministic aeat CLI. Before this module that shell was one subprocess.run with NO timeout (research finding F1): a Playwright-backed live pull that stalls - a network hang, a changed AEAT DOM selector - hangs the MCP call forever, and many clients time out a tools/call well under a minute, misreading a legitimate slow pull as failure. There was also no way to terminate a hung child.

This runtime wraps the call in a per-tier timeout (ADR mcp-protocol-hardening H1): generous for the AEAT-sede / live family, tighter for local mutations, tight for local reads. On timeout it terminates the WHOLE process tree - a Playwright pull spawns a browser child, so killing only the aeat process would strand the browser - and returns a typed timed-out result the caller renders as an instructive, localized refusal. The tier is derived from the command’s own annotations, so it tracks the classification the gates already use. Migration to the (deprecated-in-v1, redesigned-in-RC) MCP Tasks mechanism is deferred until the v2 SDK is stable.

class CallTier(*values)[source]

Bases: StrEnum

The timeout tier a command runs under.

LIVE is the AEAT-sede / open-world family (a portal pull that may take minutes); MUTATE is a local state change; READ is a local read.

READ
MUTATE
LIVE
tier_for(*, read_only, open_world)[source]

Choose the timeout tier from a command’s annotations.

Open-world (AEAT-sede) verbs get the live tier regardless of read/write - a portal read can be as slow as a portal write; a local read gets the read tier; everything else gets the mutate tier.

Return type:

CallTier

Parameters:
timeout_seconds(tier)[source]

Return the wall-clock ceiling in seconds for tier.

Return type:

float

Parameters:

tier (CallTier)

class SupervisedResult(**data)[source]

Bases: BaseModel

The outcome of a supervised subprocess run.

timed_out is true when the process exceeded its tier ceiling and its process tree was terminated; stdout/stderr/returncode carry the completed process output otherwise (and best-effort partial output on timeout).

Parameters:
stdout: str
stderr: str
returncode: int
timed_out: bool
run_supervised(argv, *, timeout_s, encoding, errors='replace')[source]

Run argv with a wall-clock ceiling and process-tree termination.

The child is started in its own process group / session so its whole tree can be signalled, stdin is isolated to DEVNULL (an agent console never answers an interactive prompt), and on timeout the tree is terminated and timed_out is set. Best-effort partial output is captured after a kill.

Return type:

SupervisedResult

Returns:

The SupervisedResult.

Parameters: