loommux
loommux is a persistent, inspectable IPython MCP server for running stateful Python work and managing kernels, executions, and outputs.
Submit IPython cells with
run_cell; variables, imports, and definitions persist across subsequent cells in the same kernel.Attach to or create named persistent main kernels, and create/use child kernels for isolated or concurrent work.
Manage kernels from inside a kernel with the
loommuxclient: list, find, create, rename, recycle, and run/wait/read/search/interrupt/restart.Wait on running executions without terminating them, returning on completion, timeout, or a silence interval.
Inspect kernel, workspace, interpreter, PID, busy state, and execution lifecycle/status.
Read retained output streams (
combined,stdout,stderr,result,traceback) by line range, with per-line clipping.Search output using literal text, regex, or auto mode, with optional case-insensitivity and context lines.
Request complete output with
%%loommux --full-outputto bypass the default 5,000-token delivery limit.Interrupt the active execution or restart the kernel.
Control per-call wait/silence timers and forward cells to named child kernels via the
%%loommuxcell header.Display PNG/JPEG/WEBP/single-frame GIF images using IPython
display().
Provides tools to run and manage persistent IPython kernel sessions, enabling code execution, output inspection, and kernel lifecycle management similar to a Jupyter notebook environment.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@loommuxrun Python code to sum numbers from 1 to 10"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
loommux
loommux is a Model Context Protocol (MCP)
server for persistent, inspectable IPython work. A loommux server owns logical
kernel resources on behalf of MCP sessions. Each resource has an independent
namespace, execution history, client lease set, and replaceable kernel process.
Python variables, imports, and definitions survive from one submitted cell to
the next inside that selected resource.
The project is for MCP clients and agents that need more than a one-shot subprocess. It makes a running cell observable without losing it: callers can wait later, inspect its state, read a selected output stream by line range, search retained output, interrupt the active cell, or restart the kernel.
What It Provides
One main kernel per MCP session, private by default, shared by name when clients need one collaborative namespace, and child kernels that belong to it alone.
One persistent IPython session, execution sequence, and retained history per logical resource.
Activity, standard MCP-ping, or connection-bound leases with automatic orphan reclamation; a connection lease follows the client's open stream and alerts when the client stops behaving that way.
A strictly increasing positive integer
executioncoordinate for every accepted cell during its logical resource's lifetime.In-memory output retained separately as
combined,stdout,stderr,result, andtracebackstreams.IOPub-order
combinedoutput, including IPython-styleOut[execution]:labels for display results.Terminal-formatted IOPub text normalized into ordinary append-only text transcripts before it reaches public output.
A non-blocking execution model: a tool-call timeout ends only that MCP call; it does not terminate the Python cell.
Explicit interrupt and kernel-reset operations, with preserved historical execution records after a reset.
A single MCP entrypoint with content-only defaults and an explicit structured result mode.
loommux is intentionally not a multi-user notebook service, a durable job
queue, or a sandbox. Kernel state and execution records are memory-only and
belong to their logical resource; server shutdown retires every resource.
Related MCP server: kernel-mcp
Requirements And Installation
loommux requires Python 3.13 or newer. The installed package brings the runtime dependencies needed to launch an IPython kernel.
python -m pip install loommuxWindows
Native Windows support covers Windows 10 and Windows 11 with CPython 3.13 or newer. Install into the interpreter that the MCP host will use:
py -m pip install loommuxThe installed command is loommux.exe. Point an MCP host directly at that
executable rather than through a shell wrapper, and use an absolute Windows
workspace path for cwd:
{
"mcpServers": {
"loommux": {
"command": "C:\\workspace\\.venv\\Scripts\\loommux.exe",
"args": ["--result-mode", "structured"],
"cwd": "C:\\workspace"
}
}
}loommux launches the kernel with the same interpreter as loommux.exe, keeps
its IPython and Jupyter state in a private temporary directory, and uses a
Windows Job Object so restart and server shutdown also end child
processes launched by the kernel. A submitted cell remains arbitrary Python:
commands inside that cell must target the operating system on which the kernel
is running. WSL is a separate Linux deployment, not a substitute for native
Windows coverage. Because IPython kernels do not accept Ctrl+C through this
entry point on Windows, interrupt replaces the private kernel after
marking the active cell interrupted; later cells use the fresh kernel.
The package installs one console command:
loommux content-only results over Studio stdio
loommux --server content-only results over Streamable HTTPloommux uses an MCP Studio or host's child-process stdio connection by
default. --server starts a Streamable HTTP service with configurable --host,
--port, and --path. Both forms return only content by default.
--result-mode structured is the explicit opt-in that additionally returns
structuredContent.
For development, use uv:
git clone https://github.com/MichengLiang/loommux.git
cd loommux
uv sync --locked --group devDefault Studio Connection
loommux defaults to MCP stdio transport and returns model-oriented content
only. This is the Studio-compatible default and prevents a client from
preferring raw structuredContent over the presentation intended for the
model.
The server process's working directory is the default kernel workspace. A
generic MCP configuration therefore assigns the desired project directory as
the command's cwd:
{
"mcpServers": {
"loommux": {
"command": "loommux",
"cwd": "/absolute/path/to/your/workspace"
}
}
}The exact enclosing configuration shape depends on the MCP host. The material
facts are that the host starts loommux, the process runs in the intended
workspace, and the Python environment running loommux can import
ipykernel.
On startup, loommux resolves its workspace, builds a kernel launch from the server interpreter, and starts the kernel before accepting MCP tools. Server startup fails rather than exposing a partially configured execution service.
HTTP Server And Structured Opt-In
loommux --server exposes the same tools, input schemas, execution behavior,
and model-readable text over Streamable HTTP. It remains content-only unless
--result-mode structured is explicitly supplied.
Start a loopback-only content-only HTTP service from the workspace you want the kernel to use:
cd /absolute/path/to/your/workspace
loommux --server --host 127.0.0.1 --port 8801 --path /mcpIts MCP endpoint is http://127.0.0.1:8801/mcp. --result-mode structured is
available only when a client genuinely needs the raw status object:
loommux --server --result-mode structured --host 127.0.0.1 --port 8801 --path /mcpMCP Studio, Inspector, and other Streamable HTTP clients use the same endpoint URL. There is no separate Studio protocol. The complete matrix, subprocess configuration examples, and security guidance are in MCP Connection Guide.
HTTP is a deployment boundary, not a different execution model: the
tools, resource-local execution sequences, output streams, workspace
resolution, and presentation rules are the same as the stdio server. The HTTP
application also serves a resource console at / and operational JSON APIs
under /api. Binding it beyond the local machine exposes arbitrary Python
execution and requires network controls and authentication outside loommux.
If a server stops answering (the console, new connections, and Ctrl+C all go
quiet), run kill -USR1 <loommux pid> on Linux or macOS. Every thread's Python
stack is printed to the server's stderr and the server keeps running. The
handler runs below the event loop, so it works while the loop is frozen;
include its output when reporting the hang.
On Linux with glibc, a long-running server with many concurrent clients holds
memory in per-thread malloc arenas that it has already freed. Starting it with
MALLOC_ARENA_MAX=2 keeps that bounded: under the same two-minute load the
server's resident memory levelled off at 277 MB instead of growing past 600 MB.
Kernel Resources And Client Leases
An ordinary MCP connection selects a kernel without changing the eight-tool
surface. Each MCP session works in one main kernel: without an additional
header, a private one named after its MCP Session ID; with
X-Loommux-Resource, a named main kernel shared by every session configured
with that name, where each participating session still holds an independent
lease. Kernels that a kernel opens by name are child kernels of that home (see
Main Kernel Binding Design).
The server chooses how a lease proves that its client is still there with the
lease_mode setting of its configuration directory:
Mode | The lease holds while | Fits |
| tool calls keep arriving within 80 minutes (24 hours for named main kernels) | any client |
| the client pings within 60 seconds | clients that ping on their own, such as |
| the session keeps its standalone Streamable HTTP | clients that hold that stream while they run, such as Claude Code |
A connection lease survives any amount of idleness. About a minute after the
client exits (connection_grace_seconds, default 60), the session
counts as gone and its lease is released. A named main kernel then keeps
itself for the named window (named_activity_timeout_seconds, 24 hours) after the
last call, so a new session can attach to it by name; the console shows how
long it has left and can retire it at any time. An unnamed private-<session>
kernel is retired with the lease, since nobody comes back to a generated name.
If a session
calls tools without that stream, or keeps calling after it
closed, the lease falls back to the activity window and raises an alert:
every tool result of that session ends with a Lease alert line, and the
console at / turns its lease-signal row red. For Claude Code over HTTP:
loommux --server --host 127.0.0.1 --port 8801 --path /mcpstarted in a directory whose .loommux/config.py holds:
lease_mode = "connection"The server publishes the current lease policy at /api/lease-policy. The
included loommux.client.LeaseAwareClient discovers and pins that policy
generation before initialization and sends standard MCP ping while a
heartbeat lease is active; under a connection policy its transport's own
standalone stream holds the lease:
from loommux.client import LeaseAwareClient
async with LeaseAwareClient(
"http://127.0.0.1:8801/mcp",
"analysis-agent",
resource_name="shared-analysis",
) as client:
result = await client.call_tool(
"run_cell",
{"freeform": "value = 1\nprint(value)"},
)The operator label is optional. Omitting it or passing None leaves the
X-Loommux-Operator header absent, so the server uses its session-derived
fallback display name.
The complete runnable client-cooperation example is in examples/lease-aware-client.
The complete identity, lifecycle, policy, orphan-execution, control API, and source-ownership contract is documented in Kernel Resource Daemon Design.
Controlling Kernels From A Kernel
Every kernel knows which resource it is and where its daemon listens
(LOOMMUX_KERNEL_ID, LOOMMUX_KERNEL_NAME, LOOMMUX_CONTROL_URL), and holds
a client of that daemon as loommux. The daemon keeps the registry, so it
lists, finds, opens, renames and retires kernels; a kernel handle has the eight
operations of the MCP tools, with their names and parameters:
# the daemon's buttons
loommux.kernels() # every kernel: name, id, state, busy, idle, pid, executions, leases, kept, scope, home
loommux.kernels(timeout=2) # the same, giving up with DaemonError after 2 seconds
loommux.self # this kernel
lab = loommux.create("lab") # a child kernel of this main kernel, opened if missing
loommux["lab"] # find by name, path or id; never opens
loommux["/other-task/lab"] # another main kernel's child kernel
loommux.rename(loommux.self, "report-cleanup") # returns the name received; report-cleanup-2 if taken
loommux.recycle("lab", force=True)
# a kernel's buttons
n = lab.run_cell("train()", wait_seconds=0)["execution"]
lab.wait(n, timeout_seconds=1450, silence_seconds=140)
lab.read_output(n, line_range="-20:")["text"]
lab.search_output("loss", n)
lab.interrupt(); lab.restart()
loommux.notify("⚠ memory is low") # to the agent, apart from this cell's outputA kernel opened this way is a child kernel of the asking kernel's main kernel.
It lives as long as that main kernel is kept and is released with it. Its name
is unique only among that main kernel's child kernels, so a name is a path:
/main/child from the root, a bare name from the caller's main kernel, and an
id anywhere. Names contain no /. Only an MCP session opens a main kernel
(--attach NAME --create), because only its lease keeps one. A kernel already
running a cell refuses run_cell() with DaemonError whose status is
busy; cells are never queued. %%loommux --kernel build is create,
run_cell and following its output in the forwarding cell; see the
Kernel Control Surface Design.
loommux.notify(text) is how code in a kernel, an alarm in a post_run_cell
hook for instance, tells the agent something about this moment. A notice is
not output: no output stream holds it and the 5,000-token limit never omits it.
The next tool result ends with every notice no result has delivered, each
once, labeled In [N] · when another cell spoke it and Startup · when a
startup file did. They come in one envelope that starts a line, with the
Kernel: line and the Lease alert when a result has them:
In [7] · 54.3 s:
done
<loommux-notice>
⚠ 体征|memory 2.1 GiB, past 2 GiB
Kernel: report-cleanup (opened)
</loommux-notice>The tag is the boundary between what the cell produced and what the
environment says; a client joins a result's text blocks end to end, so a
block of its own would not be one. A notice is cut at 500 characters and a cell keeps ten; a
cell forwarded with --kernel build speaks build's notices again as
build · …. The idle column, and idle_since in each snapshot, say how long
a kernel has run nothing.
The client speaks the HTTP control plane, so anything it does stays outside
the daemon process. Outside a kernel, loommux.daemon.Daemon() connects to
LOOMMUX_CONTROL_URL or http://127.0.0.1:8801 and acts for the person
running loommux: it lists every kernel and resolves a main kernel by name or
any kernel by id, opening none, since nothing there can hold a new one. A
stdio server has no HTTP control plane; its kernels still know who they are,
and every request explains that no control plane exists.
Workspace And Interpreter
Workspace selection occurs when the server process starts. loommux does not provide a runtime tool that changes the workspace or Python interpreter.
By default, the server's current working directory is the workspace and the
interpreter that launched loommux launches the kernel. This preserves the
same virtual environment that imported loommux and avoids an ambiguous second
Python-selection mechanism.
A config.py in the configuration directory that
defines resolve_workspace(launch_cwd: Path) -> Path | str chooses another
workspace; it is the only way to. loommux does not treat .git, .codex or
any other tool's marker as its own. Resolver failures prevent startup before
tools are available.
The Codex configuration directory is an example. See workspace configuration and the canonical Coding Agent Control Plane Design for the complete contract.
Configuration Directory
loommux is configured in one directory, shaped like an IPython profile
directory: the nearest .loommux at or above the directory it starts in, as git
finds .git, or the one --config DIR names. The start logs which one it uses.
.loommux/
config.py settings and resolve_workspace, read once when the daemon starts
startup/ *.py and *.ipy files every kernel runs before its first cellconfig.py is an ordinary Python module whose plain names are the settings:
Name | Default |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
It may import modules and define helpers. Any other public name stops the start
with config_unknown_setting, so a misspelled setting is reported rather than
left at its default. A relative DIR is read from the directory loommux starts
in. The environment does not configure loommux.
The files of startup/ run in name order in every kernel process: when a main
or child kernel opens, after restart, and after the replacement that stands
for an interrupt on Windows. They run as loommux's own requests with
store_history=False, so they take no execution number, and in the user
namespace, so what they define is there for the first cell; an .ipy file may
use magics such as %run. A file that succeeds is silent. One that raises, or
runs past 60 seconds and is interrupted, becomes a notice,
Startup · NAME failed: …, and the next file still runs. The directory is read
at each start, so a changed file applies to the next kernel. It is the place
for environment, things that work only if nobody has to remember to start
them; tools are one line to load when needed. See Kernel Environment
Design, and the tutorial
that builds a small environment step by step; examples/kernel-environment
is that environment, with a tour that runs it.
Execution Model
Each accepted run_cell submission creates an execution record with one
public identity:
execution: positive integerThe sequence begins at 1 for a newly provisioned logical resource and
increases only when a cell is accepted. Loommux accepts one running cell at a
time inside each resource; separate resources may execute concurrently. A
second run_cell call against the same busy resource is rejected with
status="busy"; it is not queued.
An execution can be running, completed, error, interrupted, or
killed. Python errors are recorded execution states, not MCP transport
failures. The error summary identifies the exception while the collected
traceback remains available from the execution's traceback stream.
The integer is the number IPython gives the same cell: In [5] in a result is
In[5] and _i5 inside the kernel. restart starts the count again, as a
notebook does; the records of the replaced process go with it, and what an
agent saw of them is in its own transcript. When a cell yields a text/plain
display result, the combined log carries the same coordinate:
Out[5]: 42After a reset, the replacement IPython kernel may have restarted its internal counter, but the next loommux execution number remains consecutive and prior records remain readable.
MCP Tools
All tools below are exposed by the single loommux entrypoint. Calls that take an
optional execution share one selection rule: an explicitly supplied positive
integer selects that record; otherwise loommux selects the current running
record, then the most recently accepted record. With neither, the tool returns
execution_not_found.
Tool | Purpose |
| Submit one loommux IPython cell to the persistent kernel and wait for its initial result. |
| Inspect the main kernel's name, the workspace, its authored source category, interpreter, kernel PID, busy state, and current or recent execution. |
| Inspect lifecycle and diagnostic metadata without returning the full output body. |
| Read a selected execution stream, optionally by line range and with per-line clipping. |
| Search a selected output stream using literal text or regular expressions. |
| Wait for an execution without interrupting it, until it ends, the time runs out, or it has printed nothing for |
| Send an interrupt signal to the current running execution. |
| Restart the kernel. |
Submitting A Cell
run_cell accepts one freeform loommux IPython cell and submits it exactly
as written. Its variables, imports, and other namespace state are available to
later cells in the same selected logical resource.
import math
radius = 3
math.pi * radius**2The Cell Header
A cell that needs its own control begins with a %%loommux header, written in
ordinary IPython cell-magic syntax:
%%loommux --wait 260 --silence 35
%%bash
pnpm exec playwright testThe wait has two timers, as an HTTP client has an overall timeout and a read
timeout. --wait SECONDS is the most the call waits. It only
changes how long that run_cell call waits: it does not limit Python runtime,
interrupt the cell when time expires, modify later calls, or add a variable to
the kernel. --full-output is described under
Requesting Complete Output. The options apply to
that one cell; the next cell starts from the defaults again.
--silence SECONDS is the second timer: the call also returns once the cell
has printed nothing for that long, still running, saying how long it has been
silent and showing its last lines. Whichever comes first ends the call; neither
interrupts the cell. Both default to 10 seconds, which suits a cell that ends
in a few; for a longer run, write the two estimates you hold as whole seconds
worked out from the run at hand rather than round numbers: --wait a little
above how long it took last time, --silence the longest gap between its
lines, such as its slowest test. The example's suite last ran 212 seconds and
its slowest case 23. A cell that writes only a long --wait still returns after
ten quiet seconds.
Every run_cell and wait result says on its first line how long the
execution has run, from submission to its end or to now: In [7] · 54.3 s:,
In [9] · 270 ms:. It is the measurement the next estimate starts from
(Waiting Until It Goes Quiet, section 4.6).
In the example, a healthy run returns when it finishes; one stuck at second 40 returns at
second 75 instead of 260. The wait tool takes silence_seconds, counted from
the later of the call and the last output, and so do Kernel.run_cell and
Kernel.wait; Kernel.run_cell leaves an argument it is not given to the
cell's header. Any running result shows its last few lines. Python processes a
cell starts are unbuffered (PYTHONUNBUFFERED=1), so uv run pytest under
%%bash streams its lines rather than holding them until it exits. See
Waiting Until It Goes Quiet.
The header is IPython, not transport metadata. The daemon reads its options and submits the cell unchanged, and the kernel interprets the header itself, so another cell magic may follow and IPython history keeps the header:
%%loommux --wait 0.5
%%bash
uv run pytestA malformed header returns invalid_cell_header before an execution is
allocated or source is submitted. A %%loommux line anywhere but first raises
a UsageError saying so. See Cell Header Design
for the grammar and its reasons.
--kernel NAME in the header hands the cell to that kernel:
%%loommux --kernel build --wait 75 --silence 25
%%bash
uv run pytestThe kernel named build is this main kernel's child kernel, opened on first use and
retired with the main kernel (see Controlling Kernels From A Kernel).
Its combined output is relayed into this cell as it arrives; an error there
ends this cell with a one-line ForwardedCellError beneath the relayed
traceback, and since this cell waits on it, interrupting this cell interrupts it as well. Writing the
receiving kernel's own name is the same as writing none. Relayed output is
text; rich displays such as images stay in the target's execution record.
Naming And Switching The Main Kernel
Each MCP session works in one main kernel. --attach NAME --create names it:
the session works in the main kernel NAME from this cell on, which is opened
when it does not exist and found by name when it does, whoever else works in
it. A phrase describing the task makes a good name, and the same first cell
returns to the kernel after the host was closed. Naming is what keeps a kernel
after its host closes:
%%loommux --attach report-cleanup --create
import pandas as pdWithout --attach, a session's first call opens private-<session>, and
--kernel always forwards as described above. --attach NAME alone moves the
session to an existing main kernel at any time, for example after a first cell
that ran before the agent remembered its kernel, and releases its lease on the
one it leaves:
%%loommux --attach report-cleanup
df.head()Without --create a missing name returns kernel_not_found, listing the main
kernels, and opens nothing. The call that changes the session's main kernel ends
with a line such as Kernel: report-cleanup (attached; 12 earlier executions) in its <loommux-notice> envelope, and status begins with the kernel's name. A kernel renames
itself with loommux.rename(loommux.self, "new-name"). The rules, and the terminal
multiplexers and notebook servers they come from, are in
Main Kernel Binding Design.
When the call returns while the cell is still running, use wait,
execution_status, read_output, search_output,
interrupt, or restart to continue observing or controlling the
same execution.
Output Streams And Long Output
Each execution retains five append-only text projections:
Stream | Contents |
| stdout, stderr, display results, and tracebacks in IOPub arrival order. |
| Python stdout stream events, and help IPython sends to a frontend's pager ( |
| Python stderr stream events. |
|
|
| Traceback text from Python error events. |
Completed combined output of at most 5,000 o200k_base tokens is returned by
run_cell and wait beneath an In [execution]: header. A display result then
keeps its IPython-style Out[execution]: line; a silent cell returns only the
input header, and stdout or traceback remains in its original combined order.
For an execution that is still running, or for an unmarked terminal execution
whose combined output exceeds 5,000 tokens, the response retains the record but
omits the full body. Its omission notice reports the combined output's total
lines, Unicode code point characters, and UTF-8 size using one binary unit (B,
KiB, MiB, and so on). The structured run_cell, wait, and
execution_status surfaces expose the corresponding exact counts as
output_total_lines, output_total_characters, and
output_total_utf8_bytes. The output is not discarded; the notice itself names
the recovery paths, and the caller reads or searches the retained text through
read_output and search_output, or requests the complete body with the
%%loommux --full-output header. Token counting is required for this
automatic delivery policy; if the o200k_base tokenizer cannot be loaded, the
call fails instead of silently changing to another limit.
read_output takes start:stop coordinates shaped like a slice: both endpoints
are line numbers, both are inside the range, and either may be omitted, which
leaves that end at the boundary. So : is the whole stream:
: the whole stream
:10 first 10 lines
-10: final 10 lines
20:40 lines 20 through 40
3:3 only line 3Negative endpoints count line numbers from the end and stay inclusive, so :-3
stops at the third-from-last line; it does not drop the last three. Every
selected line comes back in one response, so there is no need to divide the
read into consecutive small ranges.
max_chars clips each returned line without changing stored text or line
coordinates. search_output supports literal, regex, and auto
matching. In auto mode, loommux treats the query as a regular expression
when it compiles and falls back to literal matching when it does not. Search
results preserve original line numbers, mark matching lines with M, and
mark selected context lines with C.
Requesting Complete Output
When a cell's entire terminal combined output is the intended result, include
--full-output in its header:
%%loommux --full-output
build_report()The option applies only to that execution. Once the execution is terminal, it
bypasses the normal 5,000-token delivery threshold and makes run_cell or a
later wait return the complete collected combined output. It does not cause
partial running output to be returned and does not alter the input or behavior
of read_output and search_output.
The full-output and wait options are independent and may be combined:
%%loommux --wait 38 --full-output
build_report()Interrupting And Resetting
interrupt requests an interrupt for the current running cell. An
interrupt_sent response only confirms signal delivery; the execution reaches
its final state after the kernel reports IOPub idle.
restart is stronger: it stops the existing kernel and starts a
replacement in the same workspace with the same interpreter, which runs the
startup files again. A running cell is stopped. The execution records go with
the old process and the next cell is In [1], as IPython numbers it.
Stopping the loommux server retires all resources. Their kernels, namespaces,
execution-record tables, output streams, and sequences are not persisted to
disk. Recycling one resource has the same persistence boundary for that
resource; a subsequently provisioned resource begins a fresh sequence at 1.
Security
Arbitrary Python execution is loommux's central capability. Treat the MCP client, its process account, installed packages, the selected workspace, and any reachable network endpoint as parts of the same security boundary. Give the server access only to files, environments, and network resources the MCP client is authorized to use.
The stdio server is generally the least exposed deployment mode. The HTTP server must never be placed directly on an untrusted network. For a vulnerability in loommux itself, use the private reporting process in SECURITY.md, not a public issue.
Architecture And Documentation
The runtime is deliberately divided into narrow responsibilities:
loommux/
session.py
Owns one persistent IPython namespace, execution identity, selection,
waiting, control operations, execution records, startup files, and
the notices not yet delivered.
kernel/
launch.py
Builds the interpreter command, child environment, and private root.
runtime.py
Owns Jupyter kernel process lifecycle and platform containment.
session.py
Correlates IOPub messages with the active execution record.
execution/
record.py
Stores one execution's lifecycle facts and normalized projections.
events.py
Names visible text, image, and delivery-failure events.
logs.py
Provides append-only streams, line ranges, clipping, and search.
terminal.py
Removes terminal controls while preserving chunk boundaries.
cell_header.py
Parses the %%loommux cell header for the daemon and the kernel.
config.py
Finds the nearest .loommux/ at or above the launch directory, or the one
--config names, and reads its settings, resolve_workspace, startup/.
workspace_resolver.py
Resolves the kernel workspace before tools are exposed.
notice.py
The notice channel's MIME type and bounds, shared by kernel and daemon.
kernel_extension.py
IPython extension every kernel loads: interprets the cell header and
places the daemon client in the namespace as ``loommux``.
daemon.py
HTTP client of the daemon's control plane.
identity.py
Names the kernel identity variables the daemon passes to its kernels.
mcp/
factory.py
Registers MCP tools that consume the protocol-neutral session.
result.py
Projects session facts into MCP text, structured, and image content.
presentation.py
Renders MCP-facing model-readable text.
entrypoints.py and server.py
Select MCP transport and expose the installed command.The top-level loommux package imports only the protocol-neutral Python
session API. FastMCP is entered explicitly through loommux.mcp; the runtime
does not depend on its transport consumer.
The current public contract is documented in Coding Agent Control Plane Design. Focused references cover the cell header, main kernel binding, kernel environment, freeform cell input, complete-output control, workspace configuration, and the changelog.
Features are frozen until the rebuild onto jupyter_client's async API, AnyIO and Hypermorph live objects; Rebuild Brief is its inventory, and spikes holds the experiments it stands on.
Development And Release Checks
Run the Python checks from the repository root:
uv run pytest
uv run ruff check src tests examples scripts
uv run basedpyright src
uv build --out-dir dist
uv run twine check dist/*The project metadata declares this README as the package readme:
[project]
readme = "README.md"Consequently, the same document is rendered on PyPI when a new release is built and uploaded. The explicit source-distribution allowlist includes this file, the runtime package, tests, and public documentation while excluding workspace-only material.
See CONTRIBUTING.md for contribution expectations.
License
Copyright 2026 MichengLiang.
loommux is licensed under the Apache License, Version 2.0.
Available Tools
8 toolsexecution_statusA
返回一个 execution 的状态与元数据。
选择规则
提供 execution 时,只选择该正整数记录。省略时,先选择当前
running 记录;不存在 running 记录时,选择最近一次被接受的记录;
两者都不存在时返回 execution_not_found。
| Name | Required | Description | Default |
|---|---|---|---|
| execution | No | 要查看的正整数执行编号。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It usefully discloses fallback selection behavior and the `execution_not_found` outcome, but it does not state whether the tool is strictly read-only, what permissions are required, or what the returned status/metadata structure looks like.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the tool purpose and then presents compact selection rules. Every sentence contributes useful information, with no redundant or filler text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read-status tool, the selection and error behavior are reasonably covered. However, with no output schema and no annotations, the description leaves the return shape and behavioral constraints underspecified, and it does not address sibling-tool differentiation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the schema documents the integer parameter. The description adds meaningful semantics beyond the schema by explaining that providing `execution` selects only that record and omitting it triggers running/recent-accepted fallback behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: returning an execution's status and metadata. It is clear what the tool does, though it does not explicitly distinguish itself from the sibling tool `status` or explain when to use this instead of that one.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The selection rules explain how the optional `execution` parameter affects which record is returned, including the running/recent-accepted fallback. However, this is primarily parameter selection behavior, not guidance on when to use this tool versus sibling tools such as `status` or `read_output`.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
interruptA
向当前正在运行的 execution 发送 kernel 中断信号。
中断语义
kernel 到达 IOPub idle 后,记录才会报告 interrupted、
error 或其他最终状态。kernel 空闲时返回 idle。
Returns:
已发送信号时返回目标 execution;kernel 空闲时返回 idle。
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose meaningful behavior: the interrupt is asynchronous, the record only reports interrupted/error/other final status after the kernel reaches IOPub idle, and an idle kernel returns idle. It omits whether partial output is preserved or permissions required, keeping it below 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the purpose and then a clearly separated semantics section. The Returns block repeats the already-stated idle behavior, which is minor redundancy, but the structure is otherwise efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no annotations, no output schema, and zero parameters, the description covers the safety-relevant behavior (async completion, final status reporting, idle handling) that an agent needs before invoking it. Only minor details such as side effects on buffered output are absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4. There is nothing further for the description to disambiguate, and it adds no misleading parameter claims.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb and resource: sending a kernel interrupt signal to the currently running execution. It is clear about what happens, but it does not name the sibling it differs from, so an agent cannot tell interrupt from restart purely from this text.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase about the currently running execution implies when the tool applies, but there is no explicit instruction on when to use it instead of restart or wait, and no exclusions. The agent must infer that this cancels a running execution rather than restarting the kernel.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_outputA
读取一个 execution 的指定输出流。
选择与流
execution 的选择规则与 execution_status 相同。stream
只能为 combined、stdout、stderr、result 或
traceback。
行坐标
line_range 形如切片 start:stop:两端都是行号,都含在范围内,
端点都可以省略,省略即到边界,所以 : 就是整个流。:10 读前
10 行,-10: 读后 10 行,2:4 读第 2 到第 4 行,3:3 只读第 3 行。
负数按行号从尾部数,两端照样都含::-3 是到倒数第三行为止,不是去掉
末三行。所选范围的全部行一次返回,不必切成连续的小段。
| Name | Required | Description | Default |
|---|---|---|---|
| stream | No | 输出流;默认 ``combined``,其余值为 ``stdout``、``stderr``、 ``result`` 与 ``traceback``。 | combined |
| execution | No | 要读取的正整数执行编号。省略时使用当前记录,随后 使用最近记录。 | |
| max_chars | No | 每个返回行允许显示的最大字符数;必须为正数。超出 部分只在响应中裁切,不改变已保存文本或行坐标。 | |
| line_range | No | 行坐标 ``start:stop``,与切片同形;两端都含, ``:`` 是整条流。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
There are no annotations, so the description must disclose the tool's behavior in full, and it does disclose the valid stream values, the inclusive-endpoint slicing, how negative indices are counted from the end, and the fact that the whole selected range is returned in a single call rather than in chunks. It omits important behavior: whether line numbers appear in the response, the shape of the output, the limits on how much can be returned, and whether the tool requires permissions or errors out when the execution is missing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with a one-line purpose, then split into clearly headed sections for stream selection and line coordinates. The line-coordinate section is long, but every sentence carries the coordinate semantics, and the examples prevent misuse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a four-parameter read tool with no annotations and no output schema, the description covers how to select the execution and the stream, the line-range grammar, and the max_chars truncation rule. It does not describe what the response itself looks like, nor what happens when the execution is invalid, so a small gap remains in understanding the input, but not enough to affect the decision to call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so a baseline of 3 would be acceptable, but the description goes well beyond the schema for the most error-prone parameter: line_range. It gives concrete examples (:10, -10:, 2:4, 3:3), clarifies that both endpoints are inclusive, and specifically corrects the common mistake that :-3 means 'drop the last three lines' rather than 'up to and including the third-from-last line'. This adds real meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first line states a concrete verb and resource: read the specified output stream of an execution, and it enumerates the five valid streams (combined/stdout/stderr/result/traceback), so the agent understands what is returned. It does not explain how this differs from the sibling tool search_output, so the agent is left to infer the split between reading a range and searching.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It says the ``execution`` argument is resolved by the same rules as ``execution_status``, and it documents the fallback from current record to most recent record, which implies how to call the tool. However, it gives no explicit guidance on when to use this tool rather than search_output, run_cell, or execution_status, and it states no exclusions or prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
restartA
重启 IPython kernel。
重置边界
若存在 running execution,它会先被终止;随后旧 kernel 停止并创建替代 kernel。
Returns: 新 kernel 的状态与 PID;重启失败时返回 workspace 或 kernel 启动错误。
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses the destructive side effect (terminating any running execution, stopping the old kernel) and describes what is returned (new kernel status and PID, or workspace/kernel startup error). It omits that in-memory variables/state are lost, which is the key consequence an agent should know.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then the boundary condition, then return values — good ordering. The '重置边界' section divider is a docstring formatting artifact that adds little, keeping it from a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with no output schema, the description covers action, side effects, and return values including the failure case. Nothing essential for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes no parameters, so there is nothing to disambiguate; the baseline of 4 applies. The schema is empty and correctly so.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: restarting the IPython kernel, with the exact sequence (stop old kernel, create replacement). It does not name the closest sibling (interrupt) to differentiate the two, so it stops short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: the note that a running execution is terminated first hints at when this matters versus interrupt, but there is no explicit 'use this when…', no exclusions, and no reference to the interrupt or status siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_cellA
向持久 IPython kernel 提交一个 IPython cell。
请你使用 IPython 的思想来优雅使用本系列工具。
输入
一格 IPython cell,变量、导入和其他 namespace 状态与当前持久 IPython 会话中的后续 cell 共享。
loommux cell magic
cell 第一行写 %%loommux,给这一次调用加参数;选项只作用于这一个 cell,
下一行可以接另一个 cell magic,例如 %%bash::
%%loommux --wait 260 --silence 35
%%bash
pnpm exec playwright test等待有两个计时器,对应 HTTP 客户端的总超时与 read timeout(CI 里是 job timeout 与 no_output_timeout):
--wait SECONDS:这次调用最多等多久;
--silence SECONDS:cell 连续这么久没有任何输出就提前返回,从提交起计,每来一条输出就重新计时。
cell 跑完立即返回;计时器先到时,结果写明 running 与原因,附最后几行输出。
到点只交还控制权,cell 照常运行:接着用 wait 等(同样两个计时器),或 read_output、interrupt。
不写时两个都是 10 秒,几秒内结束的 cell 不用写。要跑更久,就按这次具体在跑的东西估两个整数秒,
不写 30、120、600 这类凑整的数:--wait 是略高于预期、超过就该回来看看的上限;
--silence 是健康运行里两行输出之间最长的间隔。上面的 e2e 上次跑了 212 秒,最慢的用例 23 秒;
训练 12 个 epoch、每个约 280 秒才打一行时,写 --wait 3500 --silence 320。
只写 --wait 时静默仍是 10 秒,安静的长任务会在 10 秒处先回来。
每个结果第一行写着这次执行跑了多久,如 In [7] · 54.3 s:,还在跑时是从提交到现在;
下一次估计照着它修正。
--full-output:让本次 execution 在终态时返回完整 combined 正文,突破默认 5,000-token 阈值限制;
在明确需要完整阅读某些文件、资料时使用,避免反复阅读的开销。
--kernel NAME 写另一个 kernel 的名字,把这个 cell 交给它::
%%loommux --kernel build --wait 75 --silence 25
%%bash
uv run pytestbuild 是这个会话主 kernel 名下的子 kernel,不存在时首次使用就开出,
跟着主 kernel 活、跟着它回收;子 kernel 的名字只在各自的主 kernel 名下唯一,
别的主 kernel 名下的写全称 /主kernel名/build。
这个 cell 就是 loommux.create("build") 再在它上面 run_cell,在这里等它、接着它的输出;
所以中断这个 cell,build 的这次执行也被中断。
每个 kernel 里都有 loommux:守护进程的客户端,好比 docker 命令之于 dockerd。
守护进程管 kernel 的生死与名字:列出、查找、开子 kernel、改名、回收;
loommux["build"] 取到的 kernel 有与这组工具同名同参的方法,你对主 kernel 做的事,对任何一台都能做。
细节在对象上:loommux?、help(loommux)。
什么时候用呢?
同时想利用Kernel本身(如果Shellwork合适优先使用)去跑长任务的同时,还想要使用主Kernel去做别的事情使用。
想要保持主Kernel命名空间干净,临时去子Kernel做探索、尝试、测试……完成后回收临时Kernel。 如果你要做的事情本身就是阻塞当前的这个任务,那么请只使用主Kernel.
主 kernel
第一个 cell 用 --attach 名字 --create 给它命名意味着向 loommux 申请持久资源,最长24小时动态刷新。
名字必须使用描述这次任务的一个短语,不带空格::
%%loommux --attach 主kernel绑定设计 --create
import pandas as pd--attach 名字 可重新切换会曾命名到的持久资源。
任务变了,在任意 cell 里顺手写 loommux.rename(loommux.self, "新名字")。
图像展示
使用 IPython display() 即可看图, 对 PNG、JPEG、WEBP 或单帧 GIF 图像有效
执行编号与后续操作
提交成功可以获得一个 execution。后续工具使用它定位当前持久 IPython 会话中的这次执行。
| Name | Required | Description | Default |
|---|---|---|---|
| freeform | Yes | 原始 IPython cell 源码文本。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it states the two timers (--wait and --silence, both defaulting to 10 seconds), that reaching a timer only returns control while the cell keeps running, that --full-output raises the default 5,000-token combined-body cap, that interrupting this cell interrupts the child kernel's execution, and that a named resource lives up to 24 hours. It even describes the first line of each result (e.g. "In [7] · 54.3 s:") so the agent can recalibrate estimates. Remaining gaps, such as error/exception formatting or concurrency limits across kernels, keep it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first sentence and the sections are labeled, which helps navigation. However, the text is very long and includes material not needed to invoke run_cell, such as the docker-versus-dockerd analogy, renaming conventions, and pointers to help(loommux), which dilute the operational content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema and no annotations, yet the description covers what an agent needs: return shape on completion versus timeout, the execution handle used by follow-up tools, output size limits, kernel targeting and lifecycle, and image display via display(). Nothing essential for a correct call is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema documents the single freeform parameter at 100% coverage as raw IPython cell source, so the baseline is 3. The description goes well beyond that by specifying what belongs inside the cell: the %%loommux cell magic on the first line, its options (--wait, --silence, --full-output, --kernel, --attach --create), and that a second cell magic such as %%bash may follow. It never names the parameter itself, so it is strong but not exhaustive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first line states a specific verb and resource: submit an IPython cell to a persistent IPython kernel, with namespace state shared across subsequent cells. It further distinguishes the tool from siblings by explaining that continuation goes through wait, read_output, or interrupt, so an agent can tell which tool owns which phase of execution.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The "什么时候用呢?" section gives explicit when-to-use conditions for child kernels versus the main kernel, including an exclusion rule: if the work simply blocks the current task, use only the main kernel. It also routes continuation and cancellation to named siblings (wait, read_output, interrupt) with the conditions that select each.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_outputA
在一个 execution 的指定输出流中搜索文本或正则表达式。
选择与匹配
execution 的选择规则与 execution_status 相同;可选的
stream 值与 read_output 相同。query_mode="literal"
按字面文本匹配,query_mode="regex" 要求 query 是有效正则,
query_mode="auto" 先按正则解释,仅在编译失败时回退到字面匹配。
命中上下文
每条命中保留原始所选流行号,并以 M 标记;前后上下文行以
C 标记。
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | 要匹配的字面文本或正则表达式。 | |
| stream | No | 输出流;默认 ``combined``,其余值为 ``stdout``、``stderr``、 ``result`` 与 ``traceback``。 | combined |
| execution | No | 要搜索的正整数执行编号。省略时使用当前记录,随后 使用最近记录。 | |
| max_chars | No | 每个返回行允许显示的最大字符数;必须为正数,且只 裁切响应文本。 | |
| query_mode | No | 匹配解释方式:``literal``、``regex`` 或 ``auto``; 默认 ``auto``。 | auto |
| ignore_case | No | 为 true 时忽略大小写。 | |
| context_after | No | 每个命中之后附加的相邻行数;必须大于或等于 0。 | |
| context_before | No | 每个命中之前附加的相邻行数;必须大于或等于 0。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description does not include any annotations, so it carries the full burden of disclosing behavioral traits. It explains how the tool selects executions (current or most recent when omitted), how matching modes work, and how context lines are added. However, it does not disclose potential side effects (e.g., it is read-only), whether it modifies any state, or any error conditions. The description is mostly about operation mechanics, not behavioral caveats, so it partially covers transparency but leaves gaps.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear section headers for '选择与匹配' and '命中上下文', and the prose is concise. Each sentence provides useful information without redundancy. The description is appropriately sized for the complexity of the tool, covering key aspects without unnecessary verbosity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity of this tool (search with modes, context lines, stream selection) and no output schema, the description is quite complete. It provides enough information for an agent to understand how to use it correctly. It references sibling tools for parameter selection, which reduces the need for repeating those rules. However, it could mention that this tool is read-only (non-destructive) and what the return format looks like (a list of matches with context), but the absence of an output schema is somewhat compensated by the description. Minor gaps exist but overall it's sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema covers 100% of the parameters with descriptions. The description adds further semantics by explaining the selection rules for 'execution' (same as execution_status) and 'stream' (same as read_output), which are not fully apparent from the schema alone. It also clarifies the behavior of query_mode with examples (literal vs regex vs auto fallback). However, some parameters like max_chars have a schema description already, and the additional description of truncation is consistent but not substantially new. Overall, the description adds meaningful context beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: searching text or regex in specified output streams of an execution. It names the resource ('execution'), the action ('search'), and the target (output stream), which distinguishes it from sibling tools like read_output (which reads without searching) and execution_status (which retrieves status). However, it does not explicitly name a sibling tool for contrast, though the references to read_output and execution_status in the description imply differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage guidance: it explains how to select the 'execution' parameter (same rules as execution_status), how to choose the 'stream' parameter (same values as read_output), and how query_mode behaves with literal vs regex vs auto. It also explains context lines and max_chars truncation. However, it does not explicitly state when to use this tool instead of alternatives like read_output, though the contrast is implied by describing search-specific features.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
statusA
返回 workspace、kernel 与最近执行记录的观察状态。
状态范围
还返回当前 IPython 工作台启动时解析的 workspace、其
workspace_resolution 来源类别、Python 解释器、kernel PID、kernel 是否已启动,以及 kernel 是否正忙。
workspace_resolution 只能是 launch_cwd 或explicit_config;
忙碌时 current_execution 是正在运行的正整数执行编号;
空闲时 recent_execution 是最近一次被接受的执行编号。
IPython 通过原生 ZMQ 协议连接,拥有完整能力。
Returns: 当前 IPython 工作台与 kernel 的状态快照。
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses the returned fields, the enum-like invariant for workspace_resolution (launch_cwd | explicit_config), and the busy-vs-idle execution-number rule, which is genuinely helpful. It never states explicitly that the call is side-effect free or non-blocking, and the closing 'IPython 通过原生 ZMQ 协议连接,拥有完整能力' line describes the environment rather than this tool's behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded with a clear one-line summary and organized under a 状态范围 heading, but the trailing 'Returns:' sentence repeats the opening sentence verbatim in substance, and the ZMQ 'full capability' note does not earn its place in a status tool's description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description must describe the return payload and it does enumerate the key fields and their invariants. The remaining gap is that a sibling (execution_status) overlaps in apparent purpose and the description offers no boundary between them, which an agent would need in order to choose correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so per the baseline this dimension starts at 4. No parameter meaning is required from the description and none is missing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a concrete deliverable (a snapshot of workspace, kernel and recent-execution state) and the body enumerates the specific fields returned: workspace_resolution, Python interpreter, kernel PID, started/busy flags, and current vs recent execution ids. It does not, however, distinguish itself from the sibling execution_status, which on name alone sounds like the same operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: an agent can infer this is the read-only state probe to call before or after run_cell/interrupt/restart, but the description never says when to use it, when not to, or how it relates to execution_status. The current_execution/recent_execution distinction is useful context but is framed as data semantics rather than selection guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
waitA
等待一个 execution 结束,或两个计时器之一到点。
两个计时器
与 cell 头的 --wait / --silence 相同,对应 HTTP 客户端的总超时与 read timeout:
timeout_seconds 是这次调用最多等多久;silence_seconds 是 execution 连续这么久
没有新输出就提前返回,从这次调用开始计。默认都是 10 秒。
到点只结束这次调用,cell 照常运行,结果写明 running 与原因,附最后几行输出;
之后可以再调用本工具,或用 read_output、interrupt。
结果第一行写着它从提交到现在跑了多久;对照预期估出剩下的整数秒,例如
wait(timeout_seconds=170, silence_seconds=35)。
选择
execution 的选择规则与 execution_status 相同。
完整输出交付
当所选 execution 的 cell 头含 --full-output 且已达到终态时,本工具
直接返回完整 combined 正文,可以突破 5,000-token 省略;仍在运行的
execution 继续返回 running。
| Name | Required | Description | Default |
|---|---|---|---|
| execution | No | 要等待的正整数执行编号。省略时使用当前记录,随后 使用最近记录。 | |
| silence_seconds | No | execution 连续这么久没有新输出就返回,正数;从这次调用开始计。 | |
| timeout_seconds | No | 这次调用最多等待的秒数,正数。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden and does so well: it discloses the partial-return semantics (result marked running plus reason and trailing output), the elapsed-time header, and the non-obvious rule that --full-output cells bypass the 5,000-token truncation only once terminal. These are exactly the behaviors an agent cannot infer from the name.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a one-sentence summary followed by headed sections that each earn their place, and the '选择' section is an efficient one-line delegation. Minor redundancy: the two timers are introduced in the opening line and then re-explained in the first section.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With three optional params, no annotations and no output schema, the description fills every gap an agent needs: it describes the response shape (elapsed-time first line, running plus reason, trailing output), the truncation override, and the retry path. Nothing essential for calling it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, yet the description adds real meaning beyond it by framing timeout_seconds as a total/HTTP-client timeout and silence_seconds as a read timeout measured from call start, both defaulting to 10. The worked example `wait(timeout_seconds=170, silence_seconds=35)` shows how to size values against the reported elapsed time, which the schema alone does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — wait for an execution to finish — and immediately names the two independent mechanisms (total timeout vs. silence) that bound the call. It also distinguishes itself from siblings by naming read_output and interrupt as follow-up options, so an agent can tell it apart without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly explains that a timer expiry only ends this call while the cell keeps running, and tells the agent what to do next (call again, read_output, interrupt). It defers the `execution` selection rule to execution_status explicitly, which is a useful pointer, but there is no guidance on error or not-found cases.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.4.0- Changed
execution_status1 field changed- changed
Input schema / properties / execution / descriptionPrevious value: -"要查看的正整数执行编号。省略时使用当前记录,随后\n使用最近记录。"New value: +"要查看的正整数执行编号。"
- Changed
read_output1 field changed- changed
Input schema / properties / line_range / descriptionPrevious value: -"``start:stop`` 行范围;已确定需要完整消费所选流时省略,\n工具会一次返回全部行,无需拆分为多个小范围。"New value: +"行坐标 ``start:stop``,与切片同形;两端都含,\n``:`` 是整条流。"
- Changed
wait3 fields changed- added
Input schema / properties / silence_secondsAdded value: +{ + "default": 10, + "description": "execution 连续这么久没有新输出就返回,正数;从这次调用开始计。", + "type": "number" +} - changed
Input schema / properties / timeout_seconds / defaultPrevious value: -30New value: +10 - changed
Input schema / properties / timeout_seconds / descriptionPrevious value: -"本次调用最多等待的正数秒数;默认 30 秒。"New value: +"这次调用最多等待的秒数,正数。"
16 tool updates
v0.1.13- Added
execution_status - Added
interrupt - Removed
interrupt_python - Removed
python_execution_status - Removed
python_status - Added
read_output - Removed
read_python_output - Removed
reset_python - Added
restart - Added
run_cell - Removed
run_python - Added
search_output - Removed
search_python_output - Added
status - Added
wait - Removed
wait_python
8 tool updates
v0.1.11- First observed
interrupt_python - First observed
python_execution_status - First observed
python_status - First observed
read_python_output - First observed
reset_python - First observed
run_python - First observed
search_python_output - First observed
wait_python
TDQS
Scored across 8 tools
Most tools are clearly distinct (run_cell, wait, interrupt, restart, read_output, search_output), but execution_status and status overlap heavily — both report status about the kernel/executions, with fuzzy boundaries that force the agent to reason about which one answers its question. The verbose descriptions help clarify but don't fully resolve this confusion.
All tools use snake_case and a readable action-oriented convention (run_cell, read_output, search_output, interrupt, restart, wait). The main deviation is the pair 'status' vs 'execution_status', where one is a bare noun and the other a noun_noun compound, creating slight inconsistency.
Eight tools is well-scoped for a persistent IPython kernel session manager, covering execution, waiting, output inspection, search, interrupt, restart, and status without bloat. Each tool earns its place in the workflow.
The execution lifecycle is well covered (submit, wait, read, search, interrupt, status, restart). However, kernel lifecycle operations mentioned in the docs — create/attach, rename, list kernels, and recycle/kill — are exposed only via the Python loommux object, not as tools, leaving gaps an agent must work around through run_cell code.
Maintenance
Related MCP Connectors
Hosted MCP runtime where your agent publishes its own tools by tool call and keeps working.
Persistent memory and cross-session learning for AI coding assistants (hosted remote MCP).
Hosted MCP memory for coding agents: persistent across sessions, editable markdown, team sharing.
System-of-record notebook for AI coding agents: pages, datastores, tasks, skills over MCP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides persistent IPython shell sessions per conversation with DataFrame-centric architecture, enabling stateful data analysis, CLI tool execution, and integration of external MCP servers within the same workspace context.23Apache 2.0
- FlicenseNot gradedqualityDmaintenanceProvides a persistent Jupyter kernel for executing code, inspecting variables and dataframes, and checking SQL query plans, enabling agents to work with stateful Python sessions.-
- AlicenseBqualityBmaintenanceProvides a persistent, stateful IPython execution environment for MCP clients, allowing agents to run Python code, define functions and classes, and maintain state across calls. It includes tools for execution, inspection, dynamic tool registration, and robust timeout/recovery handling.11MIT
- AlicenseNot gradedqualityCmaintenanceEnables LLM clients to run Python code in a persistent, user-selected interpreter via MCP, with tools for data loading, summaries, regressions, diagnostics, and plotting, while keeping sessions alive between calls and isolating crashes.MIT