Skip to content

fix(coding-agent): restart dead kernels on ensure() and read mcp>=2 tool schemas - #2034

Open
snimu wants to merge 2 commits into
mainfrom
fix/python-runtime-defects
Open

fix(coding-agent): restart dead kernels on ensure() and read mcp>=2 tool schemas#2034
snimu wants to merge 2 commits into
mainfrom
fix/python-runtime-defects

Conversation

@snimu

@snimu snimu commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Two Python-runtime defects:

  • A dead kernel no longer wedges the session. IpythonKernelProvisioner.ensure() memoizes the started kernel and cleared that memo only on startup failure, dispose, or kill. A kernel that died after a successful startup (OOM kill, os._exit, crash) was handed to every later Python call forever — each one failing against a dead process until the session restarted. ensure() now drops the memo when the started kernel is no longer running, so the existing startup path (snapshot restore included) reruns on the next call. Fixes the defect reported in discussion [Bug] v0.8.0 stopped IPython kernel remains permanently cached after unexpected exit #1659.
  • Skill-MCP tools advertise their real input schemas again. mcp>=2 renamed the Tool schema field to input_schema (with inputSchema as the wire alias); pydantic attribute access is by field name, so mcp_base.py's getattr(t, "inputSchema", ...) was always None and every skill-MCP tool advertised an empty {} schema — models called tools blind. The lookup now mirrors the dual lookup its sibling mcp.py already uses. Fixes the defect reported in discussion Fix: McpIntegration.list_tools() always returns empty inputSchema (#1073) #1757.

Validation

  • two pins, each verified fail-unfixed: a memoized not-running kernel forces a restart (pre-fix: the dead memo is returned and no restart happens), and a Tool exposing only snake_case input_schema surfaces its schema through list_tools() (pre-fix: {})
  • suites: ipython-provisioner (20) and test_mcp_base (18 via uv run python -m unittest) — green
  • root npm run check passes via the pre-commit hook

LOC

Src +14/−5 (both point fixes; the kernel fix is a 4-line liveness check ahead of the existing memo, the schema fix mirrors the sibling module's lookup). Tests +42, changelog 1 fragment.

Linear: RES-1266 https://linear.app/primeintellect/issue/RES-1266


Note

Medium Risk
Changes Python kernel provisioning after crashes (session recovery path) and MCP tool metadata exposed to models; behavior is narrow and tested but touches execution reliability.

Overview
Fixes two runtime issues: dead Python kernels wedging sessions and empty MCP tool schemas under mcp>=2.

Kernel lifecycle: Adds isDefunct on KernelClient / ReplKernelManager (terminal shutdown state). IpythonKernelProvisioner.ensure() now clears its memoized manager when the started kernel is defunct, so the next Python call goes through normal startup (including snapshot restore) instead of reusing a crashed process. Kernels that are only temporarily not running—e.g. mid protocol repair—stay memoized because they are not defunct.

Skill MCP: McpIntegration._ensure_tools in mcp_base.py resolves tool input schemas via input_schema first, then inputSchema, matching mcp.py. Only dict-shaped schemas are exposed in list_tools() metadata.

Tests cover defunct vs repairing memo behavior and snake_case schema surfacing; changelog fragment added.

Reviewed by Cursor Bugbot for commit b7e45c4. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Restart dead kernels on ensure() and read input_schema for mcp>=2

  • IpythonKernelProvisioner.ensure checks the new isDefunct getter on cached kernel managers. If defunct, it clears the memo and starts a fresh Python process; non-defunct managers stay memoized even during protocol repair.
  • McpIntegration._ensure_tools reads the mcp>=2 input_schema attribute first, falling back to inputSchema. The resolved schema remains exposed under the existing inputSchema metadata key.

Macroscope summarized b7e45c4.

…ool schemas

IpythonKernelProvisioner.ensure() cleared its memo only on startup failure, dispose, or kill; a kernel that died after a successful startup (OOM kill, os._exit) was handed out forever, wedging every later Python call. The memo now drops when the started kernel is no longer running, so the existing startKernel path (snapshot restore included) reruns. Separately, mcp>=2 renamed the Tool schema field to input_schema; mcp_base.py read only the inputSchema alias, so every skill-MCP tool advertised an empty schema. It now mirrors mcp.py's dual lookup.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b94d987. Configure here.

Comment thread packages/coding-agent/src/core/tools/ipython.ts
Protocol repair cycles the same manager through idle/starting while it respawns its child; the broad !isRunning check would abandon it and start a second kernel over the same snapshot directory. The memo now drops only for a defunct (shutdown-state) manager - the state an unexpected child exit parks in permanently.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant