Skip to main content

loommux

CI PyPI Python >=3.13 License

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

  • Session-private kernel resources by default and explicitly named shared resources when clients need one collaborative namespace.
  • One persistent IPython session, execution sequence, and retained history per logical resource.
  • Activity or standard MCP-ping leases with automatic orphan reclamation.
  • A strictly increasing positive integer execution coordinate for every accepted cell during its logical resource's lifetime.
  • In-memory output retained separately as combined, stdout, stderr, result, and traceback streams.
  • IOPub-order combined output, including IPython-style Out[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.

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 loommux

Windows

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 loommux

The 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 HTTP

loommux 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 dev

Default 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 /mcp

Its 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 /mcp

MCP 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.

Kernel Resources And Client Leases

An ordinary MCP connection selects a resource without changing the eight-tool surface. Without an additional header, its MCP Session ID addresses a private workbench. X-Loommux-Resource selects a named shared workbench; every participating MCP session still holds an independent lease.

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:

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 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.

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.

LOOMMUX_WORKSPACE_CONFIG is the only optional workspace configuration entrance for the Python/IPython loommux server. Set it to the absolute path of a trusted Python resolver defining resolve_workspace(launch_cwd: Path) -> Path | str. loommux never searches or executes loommux_workspace.py, .codex, or any other workspace-tree file or marker. Resolver failures prevent startup before tools are available.

The generic and Codex resolver examples are inert until explicitly selected through that environment variable. See workspace configuration and the canonical Coding Agent Control Plane Design for the complete contract.

Execution Model

Each accepted run_cell submission creates an execution record with one public identity:

execution: positive integer

The 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 owned by loommux rather than copied from IPython's kernel-local execution counter. It stays stable for the logical resource, including across restart. When a cell yields a text/plain display result, loommux authors the combined log with its own stable coordinate:

Out[5]: 42

After 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
run_cell(freeform) Submit one loommux IPython cell to the persistent kernel and wait for its initial result.
status() Inspect the workspace, its authored source category, interpreter, kernel PID, busy state, and current or recent execution.
execution_status(execution=None) Inspect lifecycle and diagnostic metadata without returning the full output body.
read_output(...) Read a selected execution stream, optionally by line range and with per-line clipping.
search_output(...) Search a selected output stream using literal text or regular expressions.
wait(execution=None, timeout_seconds=30) Wait for an execution without interrupting it.
interrupt() Send an interrupt signal to the current running execution.
restart() Restart the kernel while preserving execution records and the resource-local sequence.

Submitting A Cell

run_cell accepts one freeform loommux IPython cell. Ordinary source and the resulting Python values of validated Apply Patch literals are available to later cells in the same selected logical resource.

import math
radius = 3
math.pi * radius**2

Apply Patch Literals

Use an outer triple-double-quoted literal containing a valid Apply Patch program to pass patch text through a Python cell. The exact *** Begin Patch and *** End Patch markers, valid file-operation controls, and hunk lines are validated before loommux converts the literal into an equivalent Python str. The patch text remains part of the resulting value, including embedded triple quotes, backslashes, and braces.

patch = f"""
*** Begin Patch
*** Update File: example.py
@@
+message = r"""
+hello
+"""
*** End Patch
"""

patch contains the complete Apply Patch program. The outer r and f prefixes do not apply raw-string or f-string interpretation to the converted patch text. Marker-shaped text with invalid patch grammar is ordinary Python source and is not converted. See Apply Patch Literal Transform Design for the full contract and acceptance rules.

The default initial wait for one MCP call is 10 seconds. A cell can make its complete submission policy explicit with a Loommux control directive:

# loommux: --wait 120
build_report()

--wait 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. A malformed or duplicated option returns invalid_loommux_directive before an execution is allocated or source is submitted. Valid directive lines are transport-only metadata: Loommux consumes them before IPython receives the cell, so they do not appear in IPython history or execution records. A directive may therefore precede a %% cell magic.

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
combined stdout, stderr, display results, and tracebacks in IOPub arrival order.
stdout Python stdout stream events.
stderr Python stderr stream events.
result text/plain from IPython execute-result and display-data events.
traceback 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; read or search it through the output tools. 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 uses start:stop inclusive line coordinates. Positive endpoints are 1-indexed, endpoints may be omitted, and negative endpoints count from the end of the selected stream:

:10    first 10 lines
-10:   final 10 lines
20:40  lines 20 through 40
3:3    only line 3

When the caller has determined that the selected stream must be consumed in full, omit line_range. read_output returns all of its lines 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 a Loommux control directive:

# 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 appear in the same directive:

# loommux: --wait 120 --full-output
build_report()

They may also be split across two directives:

# loommux: --wait 120
# loommux: --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. A running record is marked killed. Reset does not erase stored executions, their output, or the sequence counter, so historical records can still be read by their integer execution value and the next accepted cell receives the next number.

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, and historical execution records.
    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.
    submission/
        cell.py
            Combines directive consumption and source transformation into one
            prepared cell passed to the session.
        directives.py
            Parses and consumes submission-owned control directives.
        apply_patch_literals.py
            Converts valid Apply Patch literals before kernel submission.
    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 workspace authorization and resolution modules remain at the package boundary until their independent configuration work is complete. They are deliberately not mixed into this structural change.

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 freeform cell control, complete-output control, workspace configuration, and the changelog.

Development And Release Checks

Run the Python checks from the repository root:

uv run pytest
uv run ruff check src tests examples
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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

loommux-0.1.13.tar.gz (229.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

loommux-0.1.13-py3-none-any.whl (69.3 kB view details)

Uploaded Python 3

File details

Details for the file loommux-0.1.13.tar.gz.

File metadata

  • Download URL: loommux-0.1.13.tar.gz
  • Upload date:
  • Size: 229.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for loommux-0.1.13.tar.gz
Algorithm Hash digest
SHA256 801dbaea24d9bcb038cb1445b68d140d1e4f4be4278fbe18871e4c62c34ef595
MD5 d6dcc0ad28fd60879b2c90e2164c87ff
BLAKE2b-256 ecbabe639abf9b771bd2f6b510ba66888bcd46d234a77f8a833db77488271cf1

See more details on using hashes here.

File details

Details for the file loommux-0.1.13-py3-none-any.whl.

File metadata

  • Download URL: loommux-0.1.13-py3-none-any.whl
  • Upload date:
  • Size: 69.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for loommux-0.1.13-py3-none-any.whl
Algorithm Hash digest
SHA256 6eab91cfa88d7f41db0e7317170ecd8f4bfccd248549c4c0f212de1c19c3e0fd
MD5 927eefca55619e93f4297143ab0e07f4
BLAKE2b-256 10ee18261d66ee8a1208ca63c30a29228ac111b1cf374bafa33fdbc2aa2c5316

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.13 This release

2 files

0.1.12

2 files

0.1.11

2 files

0.1.10

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page