simple-sage-mcp
Run and manage persistent SageMath/Python work in a long-lived kernel with session state, long-running jobs, and file execution.
Execute Sage/Python code in a persistent per-session namespace; optionally disable Sage preparsing.
Start long-running computations that continue after
exectimes out, returning a job id for later collection.Collect incremental job output with
poll, using offsets and optional waiting.Inspect kernel, session, job, client, error, limit, and log status with
status.Interrupt a running computation with
SIGINTwhile keeping session variables; force-stop the whole kernel as a last resort.Reset a session’s namespace or drop idle sessions.
Run existing files with auto-detected or explicit interpreters, arguments, stdin, cwd, and timeout; return exit code, duration, stdout, and stderr.
Provides a persistent SageMath kernel that agents can use to execute Sage code, poll long-running computations, interrupt or reset sessions, inspect kernel status, run script files, and process SageMath/Jupyter notebooks.
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., "@simple-sage-mcpfactor 2^128 - 1 and show me the result"
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.
simple-sage-mcp
An MCP server that gives an agent a persistent SageMath kernel over stdio: one long-lived Sage process that code is run in, interrupted, inspected and stopped. Work outlives the call that started it, and the kernel outlives the server that spawned it.
Quickstart
SageMath must be installed, and its own interpreter must be able to import sage.all. From a checkout:
./install.shThat finds a working interpreter, writes the sage entry into the mcp.json next
to the install target, and proves the registration works by spawning the server
and running 2+2 through it. Another client needs the block it prints:
{
"mcpServers": {
"sage": {
"command": "/path/to/python-with-mcp/bin/python",
"args": ["/path/to/sage-mcp/sage_mcp.py"],
"env": {"SAGE_PYTHON": "/path/to/sage-env/bin/python", "SAGE_CWD": "/tmp"},
"timeout": 600
}
}
}pip install git+https://github.com/JGCoelho/simple-sagemath-mcp works too. Check a
registration at any time with ./install.sh --check or
simple-sage-mcp-check /path/to/python /path/to/sage_mcp.py. sage_mcp.py --help
lists the options and the environment variables.
Related MCP server: MCP SageMath Server
Scope and safety
This is a general kernel, not a curated math API, and not a sandbox. Code sent
to exec, run_file or notebook runs as you, on your machine, with your
files and your network: the kernel is an ordinary Sage process, and
SAGE_MCP_LIMITS bounds its resources, not its privileges. There is no allow-list,
no seccomp, no filesystem or network isolation, and nothing stops a job from
reading ~/.ssh or writing anywhere you can. So: run work you would run yourself,
do not drive it from untrusted input, and there is no HTTP transport to expose --
it is a stdio server over a private Unix socket, and that is the whole surface.
Linux only: it relies on AF_UNIX, /proc and rlimits.
Persistent state and sessions
One kernel per state directory
The kernel is a separate process that the server spawns on demand and then talks to
over a private socket. Its identity is the state directory,
${SAGE_MCP_STATE:-/tmp/sage-mcp-$UID}:
kernel.sock the listener (0700, AF_UNIX -- nothing on loopback to dial)
kernel.key 32-byte HMAC key the clients must present (0600)
kernel.json pid, start time, log path -- what `status` reads when the kernel is busy
running.json the job in flight, rewritten per job, so `status`/`interrupt` still work
kernel.log the kernel's own log (rotated at `SAGE_MCP_LOG_MAX`, 8 MiB by default)Everything that points at that directory shares one kernel: several Pi sessions,
subagents, a restarted server. Jobs queue on it and run one at a time, on the
kernel's main thread. The first exec starts it (status says not running until
then); when the last server exits and the kernel is idle it stops itself. Point
two configurations at the same SAGE_MCP_STATE and they share the kernel; point
them at different ones and they cannot see each other.
Nothing lives in the package directory, and nothing is stored per call: run_file
and notebook work on the files you name, in place.
Sessions are namespaces inside that kernel
A session is a name for a namespace, not a process: session="default" unless
you say otherwise, any other name is created the first time it is used. Each
namespace starts as a copy of from sage.all import *, and from then on holds only
what that session's own code defines. So a = 1 in session work is invisible in
default, and both persist between calls -- the whole point of the kernel.
reset empties one namespace (it does not wait for the session to go idle, so do
not reset a session whose job you still want). drop frees the name, refusing a
session with a job in flight. Neither restarts the kernel, and neither touches other
sessions.
What survives what
event | variables | job in flight | counters |
| kept | keeps running; | live |
the agent's turn ends | kept | keeps running | live |
the MCP server exits or is restarted | kept -- the next server adopts the kernel | keeps running; the new server's | live in the kernel |
| kept | fails with |
|
| unaffected | unaffected (different process) | -- |
| lost | stopped mid-flight | reset with the kernel |
the kernel exits on its own, or | lost | lost | lost |
The kernel is the authority on all of it; the server keeps no state of its own
except its identity and its view of the socket. That is why a poll works after a
server restart, and why counts (jobs: N run, ...) reset exactly when the kernel
does -- counters_since says since when.
Tools
tool | params (required → | returns |
|
| stdout, then the repr of the last expression; on timeout the partial output plus a job id to |
|
| new output since the offset, ending in |
| -- | kernel/session/job/client state, last error, limits, log path |
|
|
|
|
| empty a namespace; free idle sessions |
|
| exit code and duration, then stdout/stderr inline; a non-zero exit is an error carrying both |
|
|
|
Five short transcripts with the real output -- a job outliving its exec, an
interrupt, run_file, sessions, notebooks -- are in examples/.
Jobs, and why poll exists
exec's timeout cancels nothing: a Gröbner basis or a graph enumeration that
takes twenty minutes is normal work for Sage, and a timeout that threw it away would
be a bug. So exec answers with a job id (or the finished output, if it got there
within timeout_s), and poll(job_id, since=N) returns the output produced since
offset N -- incremental, never a re-run. To cancel, do it explicitly: interrupt,
then poll to collect the KeyboardInterrupt. Silence is never read as "cancel".
A wait can be longer than your client's request timeout, so exec, poll,
run_file and notebook send an MCP progress notification every 30 s while they
wait; a progress notification resets that timeout (pi's default is 60 s, and its
mcp.json entry for sage sets "timeout": 600). A client with a tighter
timeout lowers the interval with SAGE_MCP_PROGRESS_SLICE. A client that asks for no
progress keeps the old behaviour exactly, which is why timeout_s=0 plus poll
is still the shape for a job that may run for hours.
interrupt is owner-checked: only the client whose job is running may send it.
interrupt(force=true) is not, on purpose -- it is the escape hatch for a kernel
wedged past answering its owner, so it is machine-level: any client sharing the
state directory can stop the kernel and destroy another's work. DESIGN.md, the
tool description and a test say so, so it cannot change quietly.
Why Sage, not Python
The kernel preparses by default, so this is Sage's REPL, not Python with
sage.all imported: 2^10 is 1024, 1/2 stays the rational 1/2, type(2) is
Integer, and R.<x> = QQ[] works. An ipykernel with from sage.all import *
silently gets all of these wrong.
Architecture
sage_mcp.py is the stdio front end, built on the official MCP Python SDK
(MCPServer), so schemas, validation and the protocol come from the SDK.
sage_client.py spawns and talks to the kernel: it holds a lock, presents the
authkey, and distinguishes busy (alive, not answering -- a running computation
holds the GIL) from gone, because respawning a busy kernel is how twenty minutes
of work disappears. sage_kernel.py is the long-lived process: it runs jobs on the
main thread so cysignals-based Sage (Singular, PARI, GAP) behaves, publishes
the in-flight job to running.json, and refuses to let a job's failure or an idle
SIGINT take the kernel with it. subproc.py is the child-process core behind
run_file and notebook, limits.py the SAGE_MCP_LIMITS knob. See
DESIGN.md for the reasoning and tests/conformance.py for the
protocol contract.
sage://kernel/status (application/json) is the machine-readable form of
status: pid, state, uptime, restarts, the jobs counters, last_error with
the job and session that produced it, sessions, limits (with source: kernel
when the kernel answered, configured when it did not), and the log tail -- read
from the file, so it answers while a job runs.
Requirements
Linux.
SageMath installed, separately from this server.
pip/uvxinstall the server only: the kernel is Sage's own interpreter. If it cannot be found, bothstatus(sage interpreter: NOT FOUND -- …) andexecsay so, andexec's error names the three discovery routes and the one-line fix.Two interpreters, on purpose. The server is any Python 3.10+ with the
mcppackage, and nothing else; the kernel is Sage's own interpreter, found at runtime bySAGE_PYTHON, thensage -c 'import sys;print(sys.executable)', then the python besidesageonPATH. A Sage crash cannot take the transport down, andmcpnever has to be installed into the Sage environment.notebookneedsnbformat+nbconvert+nbclientin the kernel's interpreter (they ship with SageMath).
Configuration
variable | default | meaning |
| discovered | interpreter that must |
|
| the state directory above; the kernel's identity |
| server's cwd | working directory for |
|
| per-process limits, below |
|
| GAP crash-loop guard, below |
|
| rotate |
|
| seconds between progress reports while |
SAGE_MCP_LIMITS bounds what the kernel and the children run_file/notebook spawn
may consume, so a runaway job fails instead of taking the machine down:
limit | default | scope | on trip |
| off | kernel, from just after Sage's import |
|
| 8 GiB | kernel + children |
|
| 1 GiB | kernel + children |
|
| off | per UID, machine-wide |
|
core | always | kernel + children | no core dumps |
Sizes take K/M/G; off/0 leaves the inherited limit alone; unmentioned keys
keep their default, so SAGE_MCP_LIMITS="cpu=60" only adds a CPU budget. A typo is
an error naming the knob. Only the soft limit is lowered, so a job that needs more
room gets it by restarting the kernel (interrupt(force=true)) under a different
SAGE_MCP_LIMITS. nproc is a margin over the tasks your UID already runs, armed
before each job -- not a count of the kernel's children. A job that trips a limit
fails with ResourceLimitError: RLIMIT_..., its text is prefixed with [limits],
the kernel survives, and status says which limit applies.
SAGE_MCP_GAP_LIMIT is the GAP crash-loop guard. structure_description() on
hostile input makes GAP crash-restart in a loop, forking a process per crash, and no
timeout catches it because the cell never finishes. The kernel counts the
Gap crashed / Restarting Gap markers as they are written and, at the limit,
SIGINTs that job once -- the same signal as interrupt, so it ends as a
KeyboardInterrupt, the session keeps its variables, and poll collects it. The
result is prefixed with [gap-guard] interrupted after N 'Gap crashed' markers ...,
the trip is logged, and status counts it. The evidence is the writer, not the
text: only a write from sage/interfaces/gap.py (stdout or stderr) -- or a stderr
write from a job that has already written through it -- counts, so echoing the words
cannot trip it, and importing the module (which from sage.all import * does for
every session) is not evidence. If the job catches the interrupt and runs on, the
result says SIGINT was sent but the job continued instead of claiming an
interruption that did not happen.
Tests
# 24 conformance cases, one row each, ~70 s
/path/to/sage-env/bin/python tests/conformance.py # or --only C13,C15
# 154 checks over MCP stdio through the official client, ~220 s
/path/to/sage-env/bin/python tests/acceptance.py
# one suite instead of all of them (~7 s): the names come from --list-suites
/path/to/sage-env/bin/python tests/acceptance.py --list-suites
/path/to/sage-env/bin/python tests/acceptance.py --suite discovery_visibility_suite
# 10 checks as a Sage script, ~10 s (`singular` must be on PATH)
PATH=/path/to/sage-env/bin:$PATH sage tests/interrupt_ownership.sage
# the frozen tools/list contract (8 tools, 0 mismatches)
/path/to/sage-env/bin/python tests/snapshot_tools.py --compareacceptance.py guards what is easy to regress: busy-vs-dead, spawn serialisation,
stale state files, dead launchers, an honest stop, output caps, since offsets, log
isolation, shared-kernel ownership, socket privacy and the authkey, adoption across a
server restart, the three discovery routes, a cancelled exec still being
recoverable, the run_file contract, both halves of the two-interpreter rule for
notebook, the GAP guard (writer-based, and honest about a swallowed signal), the
limits (each fails its job and leaves the kernel alive), the status resource and its
counters, and --check driving a registration in the client's own environment.
sage_mcp.py --check (behind install.sh --check and simple-sage-mcp-check) is
the 8-check smoke test; its last check runs the C01/C04/C08/C17/C20 subset of the
matrix against the registration -- its command, args and env exactly as
written -- so a registration that only works because your shell exports
SAGE_PYTHON is reported as broken, the way the client would find it. Without
tests/ (pip/uvx ship none) that subset is SKIP and left out of the totals; a
skipped check is never a pass.
Not in this release
Honest gaps, in the order they would be closed:
No CI. The suites run by hand (they are in
Testsabove). A realistic split, written down inPLAN.md§11: a fast job on GitHub-hosted runners with no SageMath (byte-compile every module,--help/--version,--suite cli_suite, the no-Sage diagnosis, andserver.jsonagainst the registry schema -- ~1 minute), and a full job inside thesagemath/sagemathimage for the kernel suites (~6 minutes plus the image pull). The fast job cannot run the conformance matrix: C01 asserts the eight tools, and without Sagenotebookis correctly not advertised.Not on PyPI yet. The wheel and sdist build clean (
python -m build,twine check) andserver.jsonis ready, but the release is deliberately unpublished for now; install from git. A published version is permanent, and the security posture ("no sandbox") has to be right before strangers run it.No Docker image. The only way to get
pip/uvxusers without SageMath working, and the honest way to make "reproducible" true.tests/is not shipped, so--check's conformance subset reportsSKIPfor an installed package (never counted as a pass).One maintainer, Linux only (
AF_UNIX,/proc,rlimits).Two
nprocchecks depend on the machine-wide task count of your UID, so a heavily loaded machine can still flake them; the test margin is 16 with 56 fork attempts to make the drift irrelevant.
License
MIT -- see LICENSE. What changed in each release: CHANGELOG.md.
Available Tools
7 toolsdropA
Free idle sessions; refuses one with a queued/running job (kept, and the job id is reported). all=true drops every idle session.
| Name | Required | Description | Default |
|---|---|---|---|
| all | No | Drop every session that has no queued/running job. | |
| sessions | No | Session names to drop. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and discloses the most non-obvious trait: partial-failure semantics, where busy sessions are kept and their job id is reported. It omits what happens to the underlying processes and whether the operation is reversible, which prevents 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?
Two short sentences, front-loaded with the main action, and a qualifier that keeps the busy-session guarantee prominent. The phrasing is telegraphic ('Free idle sessions') but nothing is wasted.
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 two-parameter, no-output-schema tool, the description covers the drop scope, the refusal behavior, and the job id feedback, so an agent can call it and interpret the result. It could say more about side effects on running processes, but nothing critical 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 100% and both parameters are documented there. The description restates that all=true covers every idle session, adding confirmation but no syntax or interaction detail beyond the schema. Baseline 3 is appropriate.
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?
Concrete verb plus resource: frees idle sessions and states the scope of 'idle'. The bare name 'drop' is ambiguous, but the description resolves it immediately. It does not explicitly differentiate from siblings like reset or interrupt, so it stops short of 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?
The condition for all=true is stated, and the refusal of busy sessions is implied as the boundary. There is no explicit routing to alternatives such as interrupt or reset when a session is busy, so the agent must infer which sibling to use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
execA
Run Sage/Python in the long-lived kernel: stdout, then the repr of the last expression (or '(no output)'). Code runs on the kernel's main thread, one job at a time, so cysignals Sage code works; state persists per session. timeout_s is NOT a computation limit: on expiry you get the partial output and a job id, and the job keeps running (collect it with poll). timeout_s=0 returns at once. A submitted job waits behind the running one ("[queued: another job is running]"); if that one holds the GIL the submit itself is refused after ~10 s ("[kernel busy ...]", the job was not submitted) -- check status or interrupt, then retry.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | Code to execute. | |
| session | No | Namespace to run in. State persists per session between exec calls. | default |
| preparse | No | Apply the Sage preparser (2^10 -> 1024, R.<x> = ...). false runs raw Python (2^10 is XOR). | |
| timeout_s | No | Seconds to wait for the result; 0 = return at once. Never cancels the job. |
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 it well: discloses single-threaded one-job-at-a-time execution, per-session state persistence, that timeout is never a cancellation, partial-output-plus-job-id semantics, queueing behavior, and the ~10s GIL refusal message. This is exactly the operational context an agent needs before calling.
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 what the tool does and what it returns, then behavioral detail. Dense but nearly every clause carries operational information; the long em-dash-heavy paragraph is slightly harder to scan than separate sentences would be.
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?
No output schema exists, yet the description spells out the return format (stdout, last-expression repr, or '(no output)'). Combined with the timeout/queue/retry semantics, nothing an agent needs in order to call or recover from this tool 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 100%, so baseline is 3, but the description adds genuine meaning beyond the schema: timeout_s is explicitly not a computation limit and yields partial output plus a job id, and session persistence across calls is reinforced. It does not further explain preparse 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?
States a specific verb and resource ('Run Sage/Python in the long-lived kernel') and even defines the output shape (stdout then last-expression repr). It distinguishes itself from poll/status/interrupt in the failure path, though it never contrasts itself with the run_file sibling, keeping it short of a full 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?
Gives concrete conditions: timeout_s=0 returns at once, on expiry you get partial output plus a job id and use poll to collect, and what to do when the kernel is busy ('check status or interrupt, then retry'). It lacks explicit guidance on when to prefer run_file over exec.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
interruptA
SIGINT the running computation: the job fails with KeyboardInterrupt and the session KEEPS its state -- the first thing to try for a runaway job. Refused when the running job belongs to another MCP client; a no-op when idle: SIGINT can never kill the kernel. force=true is the last resort instead: stop the whole kernel (graceful shutdown, then SIGTERM, then SIGKILL), losing every session's variables -- for a computation that ignores SIGINT, e.g. a wedged C-level loop. The next exec starts a fresh kernel and says so; if the signal fails the state files are kept and the pid reported, so a live kernel is never orphaned.
| Name | Required | Description | Default |
|---|---|---|---|
| force | No | Stop the kernel itself (all state is lost), not just the job. |
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: it explains the signal semantics, that state survives, that SIGINT can never kill the kernel, the exact force escalation sequence (graceful shutdown, SIGTERM, SIGKILL), the loss of every session's variables, and the failure-path guarantee that state files are kept and the pid reported. This is unusually complete behavioral disclosure for a mutation-capable tool.
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 primary action is front-loaded in the first clause, and nearly every clause adds distinct information (effects, refusals, escalation, failure path). The single dense paragraph with multiple em-dash clauses is information-rich but slightly harder to scan than a broken-out structure would be.
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 tool with no output schema and no annotations, the description covers the success path, the idle and cross-client edge cases, the forced escalation path, and what happens on signal failure. Nothing an agent needs to call this 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 100%, and the schema already defines force as stopping the kernel with all state lost. The description goes further by giving the escalation rationale for force=true and the precise consequence (losing every session's variables), which is real added meaning rather than restatement. It stops short of documenting the default value or any other input nuance, 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?
States the specific action (SIGINT the running computation) with its precise effect (job fails with KeyboardInterrupt, session keeps state), and explicitly contrasts it with the force variant that stops the kernel. An agent can distinguish this from run_file, poll, status, reset, drop, and exec 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?
Explicitly names the primary case ('the first thing to try for a runaway job') and the escalation case for force=true ('last resort... a computation that ignores SIGINT, e.g. a wedged C-level loop'). It also states two exclusion conditions -- refused when the job belongs to another MCP client, no-op when idle -- which is exactly the when/when-not guidance an agent needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pollA
Collect a job started by exec. since = the offset the previous call printed (0 to start), so you get only new output. wait_s blocks until the job finishes or that many seconds pass (0 = return immediately, the default).
| Name | Required | Description | Default |
|---|---|---|---|
| since | No | Character offset already consumed; the previous call prints the next value. | |
| job_id | Yes | Job id such as j7. | |
| wait_s | No | Block up to this many seconds waiting for the job to finish (0 = return immediately, the default). |
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 disclose key behaviors: wait_s blocks up to N seconds, 0 returns immediately and is the default, and since yields only new output. It omits what happens on polling a finished job, whether output is retained or consumed, and any permission/lifetime constraints.
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?
Three short clauses with the core purpose front-loaded and no filler. The 'since = the offset...' construction is slightly telegraphic, but every sentence carries information.
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?
No output schema exists, so the description must convey returns; it implies new output text plus a next offset value ('the previous call printed'), which is enough to drive the polling loop. It could state the return shape more directly, but an agent has what it needs to call it 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?
Schema coverage is 100%, so baseline is 3. The description largely restates the schema for 'since' and 'wait_s' (both already documented including the 0 default), adding only the operational framing of chaining offsets across calls.
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 ('Collect a job started by exec') and ties itself explicitly to the exec sibling, making clear it retrieves output rather than launching or reporting status. An agent can distinguish poll from status and exec from the description alone.
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?
Establishes the prerequisite workflow (must follow exec) and explains the incremental-polling loop via 'since' and the blocking option via 'wait_s'. It stops short of explicit when-not guidance such as whether to prefer status for a one-shot check or when polling is unnecessary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resetA
Empty a session's namespace but keep the session (see drop).
| Name | Required | Description | Default |
|---|---|---|---|
| session | No | Namespace to run in. State persists per session between exec calls. | default |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the behavioral load. It usefully distinguishes what is destroyed (namespace contents) from what survives (the session), which is the key risk distinction for a destructive tool. It says nothing about reversibility, permissions, or confirmation requirements, leaving real 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?
One front-loaded sentence with zero filler: the action comes first and the retention caveat plus sibling pointer follow. Nothing could be removed without losing information.
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 destructive, no-required-param tool with no annotations and no output schema, the description covers the core destroy/preserve semantics but omits irreversibility and any return indication. Adequate to call correctly, not complete enough to predict consequences.
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 description coverage is 100% and the single optional 'session' parameter is fully documented there, including its default and persistence behavior. The description adds no parameter meaning of its own, so baseline 3 applies.
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?
Specific verb + resource: 'Empty a session's namespace' says exactly what the operation does to what. It implicitly contrasts with the sibling tool 'drop' by noting the session is kept, though the contrast is compressed into a parenthetical.
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 clause 'but keep the session (see drop)' points the agent at the alternative and implicitly defines when to pick this over drop (you want the session retained). It does not spell out explicit exclusions or prerequisites, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_fileA
Run a file that already exists and return its output inline: exit code, duration, stdout and stderr. Nothing is copied or stored -- the file you name is the file that runs, with cwd = its own directory. lang=auto guesses from the extension and honours a #! for executables; lang=python uses Sage's own interpreter (so from sage.all import * works) and lang=sage shells out to sage. A non-zero exit or a timeout is an error, with the output attached; the timeout kills the whole process group.
| Name | Required | Description | Default |
|---|---|---|---|
| cwd | No | Working directory (default: the file's own directory). | |
| args | No | Arguments appended to the command line. | |
| lang | No | Interpreter; auto uses the extension, then the #! line. | |
| path | Yes | The file to run (absolute or relative to this server's cwd). | |
| stdin | No | Text fed to the file on stdin. | |
| timeout_s | No | Seconds before the process group is killed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral burden well. It discloses that output is returned inline, nothing is copied or stored, cwd defaults to the file's own directory, `lang=auto` and `lang=python`/`lang=sage` behave differently, non-zero exits and timeouts are treated as errors with output attached, and timeouts kill the whole process group.
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 core action and return behavior, then adds execution semantics, interpreter details, and error handling. It is dense but every sentence contributes relevant information without obvious padding.
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, so the description must cover return behavior and process execution semantics. It does this well, including output contents, error conditions, and timeout behavior, but it does not address edge cases such as output truncation, environment handling, or permission requirements.
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 already has 100% description coverage, so the baseline is 3. The description adds useful meaning beyond the schema, especially for `lang` behavior, cwd semantics, and the fact that timeout kills the entire process group, though it does not discuss `args` or `stdin`.
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: run an existing file and return exit code, duration, stdout, and stderr. It is clearly distinct from general command execution because it emphasizes that the named file is the file that runs, with cwd defaulting to its directory, though it does not explicitly name the sibling `exec` tool.
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 implied by the description: use this when running an existing file and wanting inline output. However, it gives no explicit when-not-to-use guidance and does not compare itself directly against sibling tools like `exec` or `poll`.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
statusB
Kernel state (running / busy / not running) with pid and uptime, one line per session with its name count, active jobs, the last error, the log path.
| 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 behavioral burden. It does disclose useful output detail beyond a label — the three possible state values, one line per session, and the presence of last-error and log-path fields — which implies a read-only observation tool. However, it never explicitly states that it has no side effects, does not require a session identifier, or what the output format/length looks like beyond 'one line per session'.
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?
A single tight sentence that front-loads the most important element (kernel state and its enum values) and then lists the supplemental fields. Dense but every clause carries information; only the ambiguous 'its name count' phrase is slightly muddy.
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, so the description must convey return content — which it does reasonably well. But for a tool with six action siblings, the absence of any guidance on when to invoke it (e.g., before exec or after interrupt) leaves a genuine gap.
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 the baseline is 4; the empty schema is fully consistent with a global status snapshot. The description reinforces this by describing a single aggregate report covering all sessions rather than anything that would need arguments.
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 enumerates exactly what the tool reports: kernel state (running/busy/not running), pid, uptime, and per-session details including name count, active jobs, last error, and log path. That specificity clearly separates it from action-oriented siblings like interrupt, exec, and reset. It loses a point only because it is a noun-phrase fragment with no explicit verb such as 'reports' or 'returns'.
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?
There is no statement of when to call this versus the siblings, no mention of polling it before interrupt/reset, and no exclusions. The only implicit cue is that it is a state-inspection tool, which an agent must infer from the field list.
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.
7 tool updates
v1.0.0- First observed
drop - First observed
exec - First observed
interrupt - First observed
poll - First observed
reset - First observed
run_file - First observed
status
TDQS
Scored across 7 tools
Most tools map to distinct lifecycle operations: interrupt vs. drop, reset vs. drop, and poll vs. status are well separated. The main overlap is that exec and run_file both execute code, so an agent must rely on the descriptions to choose the right context.
All names are lowercase and terse, with no casing inconsistency. However, the set mixes imperative verbs, one compound name (run_file), an abbreviation (exec), and a noun (status), so it is not a fully predictable verb_noun pattern.
Seven tools is well within the healthy range for a kernel/session management server. Each tool earns its place by covering a distinct role: execute, run file, collect output, interrupt, inspect status, reset namespace, and drop sessions.
Core lifecycle coverage is strong: execution, async polling, interruption/kill, status, namespace reset, session drop, and file execution are all present. Minor gaps exist, such as no explicit session-creation or queued-job-cancel operation, but agents can work around them with exec, interrupt, and status.
Maintenance
Related MCP Connectors
- mcp-serverOAuthai.cdbx
Build Apps and run code in 30 languages — sandboxed, with persistent sessions for agent loops.
Persistent cloud workspaces for AI agents: run commands, edit files, use git and a browser.
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
On-demand GPU nodes for agents: create nodes, run commands, and submit jobs, billed by the minute.
Related MCP Servers
- FlicenseAqualityDmaintenanceAllows LLMs to execute Wolfram Language code in a secure, session-based environment by providing an interface to interact with a Wolfram Mathematica kernel.34-
- AlicenseBqualityDmaintenanceEnables execution of SageMath mathematical computations through a local SageMath installation. Provides tools to check SageMath version and evaluate SageMath scripts with configurable timeouts and error handling.311MIT
- AlicenseAqualityCmaintenanceEnables AI agents to create, read, edit, and execute Jupyter notebook cells, manage kernels, and connect to remote Jupyter servers.2123 PyPIMIT
- AlicenseAqualityDmaintenanceEnables AI agents to execute Python, TypeScript, and JavaScript code in persistent Jupyter kernels with stateful variables and imports across interactions.74MIT