Skip to content

The session MCP

In short

  • Every session gets two MCP servers: parleml, the business tools, and session, a small server for steps, decisions, waiting and the final outcome.
  • session has four tools: run_skills, ask_gate, await_operation and report_run_outcome.
  • An agent waits on long work with await_operation and a job: handle, never by polling. Waiting costs no turns.
  • Decide automatically answers a decision only when the agent proposed a complete answer, and never approves with changes.

Every session gets two kinds of MCP server:

  • parleml — ParleML's own MCP (the tools), granted through the profile's connections with a fresh token per attempt;
  • session — the runtime's small MCP at /session-mcp/, for what no CLI provides: supervised steps, decisions, waiting on long work, and the final outcome.

The session MCP knows the caller from the bearer token the runtime gives each attempt. A tool never takes a run id, so an agent cannot act for another session, and the token stops working when the attempt ends.

Tools

Tool What it does
run_skills(runs) Starts one or more steps ({skill, instructions}) and waits for their results.
ask_gate(gate, questions, context_markdown?, subject?) Asks the person a structured question and waits for the answer.
await_operation(operation_id) Waits on a job:<experiment_id> (a run, its run_all chain included) or job:<experiment_id>/<job_id> (a batch scoring job) handle, or a deferred operation.
report_run_outcome(outcome, explanation) Records the business result. The last tool call of every run.

A CLI lists these tools under the server's name: Claude Code as mcp__session__await_operation, Gemini CLI as mcp_session_await_operation. Every briefing states the names its CLI lists (Tool names), and text the agent reads — skills, references, tool results — names a tool by its short name and its arguments by name ("the session tool await_operation (operation_id="job:…")"), never as a call such as await_operation("job:…"), which a model can copy as a tool name of its own.

run_skills

  • Only the orchestrator may start steps; a step cannot start its own.
  • A step starts in a new conversation: its instructions are the whole handoff, and its returned summary is all the orchestrator sees of it.
  • Batch several entries only for independent, read-only consultations. Steps that write share the work directory, so start them one at a time. A batch holds at most the profile's max_concurrent_children (default 5).
  • Each result has the execution status, summary, error, the step's reported outcome and requires_resume. A failed, cancelled or stopped execution overrides a reported success. A step stopped by a cap still returns what it finished, and its files stay in the work directory.

ask_gate

One question may be open per run; parallel steps may each have one.

Question kind Notes
single_select, multi_select Options are {value, label} objects. allow_free_text: true lets the person type their own answer, which the agent receives verbatim.
text Free prose.
approval Recommended option first. An affirmative option (approve, yes, proceed) is followed by approve_with_changes, which gets a required changes field; an option meaning decline (reject, no, decline, deny) gets a required reason field. Each field shows only when its option is chosen.
upload The person attaches files; the answer names their absolute paths, which the agent reads from disk.
  • default is the agent's proposal: the form opens with it filled in and marked proposed, but nothing is sent until the person submits. Approval and upload questions take no default.
  • when shows a question only after another answer matches.
  • context_markdown is the immutable record of what was decided on.
  • subject binds the answer to exact content, for example sha256:<64 hex>.
  • A gate holds at most 20 questions.

An approval with changes is an approval. The agent receives {"approve": "approve_with_changes", "changes": "…"}, applies the changes, records them in its log as conditions the person set and carries on; it asks again only if a change makes the step impossible. The workspace's decision record carries the changes as conditions, and a release approved at that gate carries them on its approval.

A decline is an instruction, not a dead end. The agent reads the reason, revises, and reopens the gate, or asks what to change when the reason is too vague.

The person answers in the session (or on the workspace's Decisions page, or from Needs attention); the route is POST /api/agent/runs/{root_id}/runs/{run_id}/answer. The answer is stored in the run's run.json before the agent sees it, so it survives a restart: if the process is gone, the session shows Resume and the answer is delivered then.

Auto-approval

Decide automatically (PUT /api/agent/runs/{root_id}/auto-approval) lets the runtime answer a gate when the agent proposed a complete answer: validated defaults for text and select questions, auto_approval_value for approvals. A required upload, a missing proposal or an ambiguous approval keeps the whole gate open for a person, with the reason shown; the runtime never submits half a gate. It never approves with changes: setting conditions is a person's answer, so auto_approval_value may not name approve_with_changes. Automatic answers are recorded as Auto-approval · System. New sessions start with auto_approval_default from the profile (off unless the profile turns it on). Auto-approval does not resume a lost process.

An automatic gate answer does not widen the business token's permissions: promotion and deletion remain unavailable through the business MCP. Launches that use paid external embedding services require a gate answered by a person; an automatic approval does not satisfy that requirement.

await_operation and job: handles

An agent waits on ParleML work with a job: handle:

  • job:<experiment_id> — a training run. A run launched with run_all (the default for a plan launch) is done when its whole train → explain → score → publish chain is, so compare and the drivers read right after it find the explanations; the result names each stage's state. An explanation computed later (run_experiment(action="explain"), another preset) holds the same handle until it is done — the result's explanations says how it went.
  • job:<experiment_id>/<job_id> — a batch scoring job. score_batch returns this exact handle as wait_handle while the job is in flight; found on disk after a server restart too.

Work a tool call left running on the server has its own handle, which the call's 202 result names as wait_handle:

  • probe:<kind>/<dataset_id>/<digest> — a Signal Check (probe_dataset).
  • eda:<dataset_id> — a dataset's EDA profile or leakage scan (prepare_data); completed says which, and that get_eda reads it.
  • autodoc:<experiment_id> — a model-card build (run_experiment(action="autodoc"), get_autodoc); completed says get_autodoc reads it.
  • call:<Idempotency-Key> — a builder call still running after its client gave up.

A job waiting for a slot — the server runs a capped number of heavy jobs (fits, explanations, scoring) at once — reports where it stands in the queue while the wait is pending.

await_operation(handle) holds the call until the work finishes, sending MCP progress every 30 seconds so the CLI does not time out, and returns its final status. Waiting this way costs no turns. Work in another workspace is refused.

One hold lasts at most PARLEML_AGENTS_AWAITABLE_MAX_HOLD_SECONDS (15 min): work still running then returns pending with waited_s — how long the run has waited on the handle, across re-awaits — and a next_action that says so and names what the agent can do if it should have finished by now: stop it (prepare_data(action="cancel") for an EDA job, run_experiment(action="abort") for a fit, score_batch(action="cancel") for a scoring job), or, for a call: handle, stop the work the call started. Awaiting again keeps waiting. The server bounds the work itself too: a job that makes no progress for PARLEML_JOBS_STALL_AFTER_S (15 min) is failed as stalled, which ends the wait with that error (failure_kind: "stalled" on a run).

A message a person queues for the run ends the wait early with a pending result carrying the message; the agent reads it and awaits the same handle again (Talking to it while it works). A stopped run, or one whose process ended, gets requires_resume; a cancelled one gets cancelled.

report_run_outcome

outcome Meaning
succeeded The requested deliverable exists and is verified.
partial An explicitly accepted, reduced scope was delivered.
blocked A named required criterion cannot be met.
failed The attempted outcome ended unsuccessfully.

The explanation is at most 20,000 characters (longer is truncated). The outcome is separate from the execution status: a run that completed may still be blocked, and a resumed attempt must report its own outcome. A run that ends without reporting one fails with missing_outcome.

The healthy call sequence

  1. Read the workspace (get_workspace_brief) and do the work through the parleml tools, or start steps with run_skills.
  2. Wait on long jobs with await_operation, not by polling: job:<experiment_id> for a run, job:<experiment_id>/<job_id> (the wait_handle) for a scoring job.
  3. Ask with ask_gate whenever a decision is the person's.
  4. Continue from the answer: after an approval with changes, apply them and go on; after a decline, revise and ask again.
  5. Verify the requested result.
  6. Call report_run_outcome last, then write the short summary.

Deferred operations

Details: Cursor's deferred operations

Cursor's MCP client cancels a tool call after about a minute, so for cursor-cli (while PARLEML_AGENTS_CURSOR_DEFERRED_MCP_WAITS_ENABLED is true) run_skills and ask_gate do not hold the call. They wait one bounded slice (PARLEML_AGENTS_CURSOR_DEFERRED_WAIT_SECONDS, 50 s), so a quick step or an instant auto-approval still returns in the same call, and otherwise return a handle:

{
  "status": "pending",
  "operation_id": "019…",
  "kind": "skills",
  "next_action": "Call await_operation with this operation_id again immediately. Do not wait, sleep, or do other work between calls: the call itself is the wait."
}

The agent passes the id unchanged to await_operation, which waits another slice. job: handles are waited on in the same slices. The terminal envelopes are:

status Means
completed result is exactly what the blocking tool would have returned.
requires_resume A step is stopped or lost its process; the person uses Resume.
failed The operation itself could not start or be read (error). A step that failed is still a completed result.
cancelled The session was cancelled.
  • The operation is stored in the run's run.json (current_operation), so it survives a restart. Calling the same tool again with the same input returns the same handle; different input is refused until the current result has been read.
  • report_run_outcome is refused while an operation is pending or its result unread.
  • Resume resumes unfinished steps first and marks their results read before the orchestrator continues.
  • A turn that only calls await_operation on the run's own handle costs no turns. The same holds on every CLI for job: waits.

Claude Code, Codex and Gemini CLI hold the call (up to PARLEML_AGENTS_MCP_TOOL_TIMEOUT_MS, 24 hours, for a gate; up to PARLEML_AGENTS_AWAITABLE_MAX_HOLD_SECONDS, 15 minutes, for a handle of server work) and never see a deferred handle.

Next