feat(mcp): non-blocking restart_kernel with progress reporting#923
Merged
feat(mcp): non-blocking restart_kernel with progress reporting#923
Conversation
…v deps Add three improvements to the MCP notebook API: 1. **create_notebook(dependencies=[...])**: Pre-install packages before first kernel launch, eliminating the add-then-restart cycle. Dependencies are added to notebook metadata and kernel is restarted once to pick them up. 2. **restart_kernel() timeout (120s)**: No longer blocks indefinitely on slow environment installations. Returns an error if deps take longer than 2 minutes instead of hanging the agent. 3. **Progress reporting**: Collects EnvProgressPhase broadcasts during kernel restart (e.g. "Downloading 3/10 packages...") and returns them to the MCP caller. Enables agents to see what's happening during large package installs. Implementation: - restart_kernel() releases state lock before LaunchKernel, unblocking other operations - Uses tokio::select! to race LaunchKernel response vs EnvProgress broadcasts - Exposes EnvProgress events to Python via ExecutionEvent subscription (previously dropped) - env_progress_message() converts progress phases to human-readable strings This fixes the issue where agents adding large packages (mlx-whisper, etc) would block for minutes with zero feedback.
The Rust implementation returns Vec<String> (progress messages) but the type stub still declared the return as None, causing ty type checker to fail in CI.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR enhances the MCP notebook workflow around dependency installation and kernel restarts by adding dependency preconfiguration at notebook creation time, introducing restart timeouts, and surfacing environment-prep progress to callers/subscribers.
Changes:
- Add
dependencies=[...]tocreate_notebook()to record deps in metadata and attempt to restart so the kernel picks them up. - Make
restart_kernel()return progress messages and enforce a 120s timeout instead of potentially blocking indefinitely. - Emit
env_progressexecution events by mappingNotebookBroadcast::EnvProgressinto the Python subscription stream.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| python/nteract/src/nteract/_mcp_server.py | Adds MCP API parameters/return fields for deps + restart progress; wires through to Python session. |
| crates/runtimed-py/src/subscription.rs | Converts daemon EnvProgress broadcasts into env_progress execution events + message formatting. |
| crates/runtimed-py/src/session_core.rs | Implements restart timeout + concurrent progress collection; changes restart return type to Vec<String>. |
| crates/runtimed-py/src/session.rs | Updates sync Python bindings to return progress messages from restart_kernel(). |
| crates/runtimed-py/src/async_session.rs | Updates async Python bindings docs/behavior for restart_kernel() returning progress messages. |
| crates/runtimed-py/Cargo.toml | Adds kernel-env dependency for EnvProgressPhase. |
| Cargo.lock | Locks the added kernel-env dependency for runtimed-py. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Comment on lines
+409
to
+413
| if let Some(NotebookBroadcast::EnvProgress { env_type, phase }) = msg { | ||
| let text = crate::subscription::env_progress_message(&phase); | ||
| progress_messages.push(format!("[{}] {}", env_type, text)); | ||
| } | ||
| // Continue waiting for launch response |
Comment on lines
342
to
+349
| /// Restart the kernel with auto environment detection. | ||
| /// | ||
| /// Returns a list of progress messages emitted during environment | ||
| /// preparation (e.g. "Installing 3 packages..."). Empty if cached. | ||
| pub(crate) async fn restart_kernel( | ||
| state: &Arc<Mutex<SessionState>>, | ||
| wait_for_ready: bool, | ||
| ) -> PyResult<()> { | ||
| ) -> PyResult<Vec<String>> { |
Comment on lines
+660
to
+662
| # Add dependencies to notebook metadata | ||
| for dep in dependencies: | ||
| await session.add_uv_dependency(dep) |
Comment on lines
+640
to
+642
| The kernel starts automatically. If dependencies are provided, they are | ||
| added to notebook metadata before the kernel launches, so the environment | ||
| is prepared on first start (no restart needed). |
Comment on lines
+664
to
+667
| # The daemon may have auto-launched a kernel (without these deps). | ||
| # Restart to ensure the kernel picks up the inline deps. | ||
| with contextlib.suppress(Exception): | ||
| await session.restart_kernel(wait_for_ready=True) |
Comment on lines
+718
to
+719
| Reports environment preparation progress (package downloads, installs) | ||
| via MCP log notifications while waiting. Times out after 120s. |
rgbkrk
added a commit
that referenced
this pull request
Mar 18, 2026
…tub types Audit of merged #923 + #924 found several issues: Rust (session_core.rs): - collect_outputs: get_cells() + find → get_cell() (single-cell lookup) - get_cell_metadata: get_cells() + find → handle.get_cell_metadata() with clone-handle-drop-lock pattern MCP server: - replace_match: get_cell() → get_cell_source() (only needs source) - replace_regex: same fix Type stubs (.pyi): - move_cell: None → str (returns new position) - get_cell: Cell | None → Cell (raises on missing, never None) - set_cell_source_hidden: None → bool - set_cell_outputs_hidden: None → bool - set_cell_tags: None → bool All fixed for both Session and AsyncSession.
rgbkrk
added a commit
that referenced
this pull request
Mar 18, 2026
…tub types (#927) Audit of merged #923 + #924 found several issues: Rust (session_core.rs): - collect_outputs: get_cells() + find → get_cell() (single-cell lookup) - get_cell_metadata: get_cells() + find → handle.get_cell_metadata() with clone-handle-drop-lock pattern MCP server: - replace_match: get_cell() → get_cell_source() (only needs source) - replace_regex: same fix Type stubs (.pyi): - move_cell: None → str (returns new position) - get_cell: Cell | None → Cell (raises on missing, never None) - set_cell_source_hidden: None → bool - set_cell_outputs_hidden: None → bool - set_cell_tags: None → bool All fixed for both Session and AsyncSession.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Improves MCP notebook API performance and user feedback when installing dependencies:
create_notebook(dependencies=[...]): Pre-install packages before first kernel launch. Eliminates the add-then-restart cycle for agents setting up new notebooks with dependencies.restart_kernel()timeout: No longer blocks indefinitely on slow environment preparation (120s timeout). Returns helpful error instead of hanging.Fixes issue where adding large packages (e.g., mlx-whisper) would block agents for minutes with zero feedback.
Test Plan
create_notebook(dependencies=["pandas", "requests"])and verify kernel launches with deps pre-installed (no restart needed)restart_kernel(), verifying timeout after ~120s and progress messages in outputenv_progressevents appear in execution event subscriptionsPR submitted by @rgbkrk's agent, Quill