mcp-forge/docs/JUPYTER_IMPLEMENTATION_STATUS.md
Hans Aschauer 14aba0a048 Implement ZMQ port management and kernel readiness checks
Container Configuration:
- Added network_mode and port_bindings to ContainerConfig
- Support for 'none', 'host', and 'bridge' network modes
- Default remains 'none' for security

Jupyter Kernel Manager:
- Dynamic port allocation for 5 ZMQ channels using socket.socket()
- _allocate_ports() finds available ports via OS binding
- Host networking mode for Jupyter kernels (network_mode='host')
- Connection file properly mounted into container
- Port bindings tracked for documentation

Kernel Readiness:
- _wait_for_kernel_ready() polls shell port until kernel responds
- Configurable timeout (30s) and poll interval (0.5s)
- Replaced time.sleep(2) with proper connectivity check
- Early return when kernel is ready

This completes the core ZMQ communication infrastructure needed
for real Jupyter kernel operation.
2026-02-07 08:16:51 +01:00

8.2 KiB

Jupyter Backend Implementation Status

Branch

feature/real-jupyter-backend

Architecture Decision

Decision: 1:1 mapping - one container per session, one kernel per container

Rationale:

  • Strongest isolation (processes, namespaces, resources)
  • Easier debugging and resource tracking
  • Clean lifecycle management
  • Optional shared volumes for file exchange between sessions

Components:

  • jupyter-client: Runs on host (MCP-Forge server)
  • ipykernel: Runs in containers
  • Communication: ZMQ protocol via Jupyter message protocol

Implementation Progress

Completed

  1. Core Architecture (kernel.py)

    • Replaced mock exec() implementation with real Jupyter protocol
    • JupyterKernelManager class with proper ZMQ communication
    • KernelInfo dataclass with connection info and client
    • Connection file generation
    • Jupyter message protocol for code execution (execute_request, stream, execute_result)
  2. Kernel Lifecycle

    • start_kernel(): Container creation, kernel startup, client connection
    • shutdown_kernel(): Graceful shutdown, container cleanup, connection file removal
    • restart_kernel(): Stub implementation (needs KernelManager integration)
  3. Code Execution

    • execute_code(): Proper message protocol execution
    • Output collection (stdout, stderr, result)
    • Error handling with traceback capture
    • Activity timestamp tracking
  4. Namespace Introspection

    • inspect_namespace(): List variables via introspection code
    • get_variable_info(): Type, size, shape, repr extraction
  5. Resource Management

    • cleanup_idle_kernels(): Time-based cleanup
    • Activity timestamp updates
  6. MCP Integration

    • start_kernel() accepts injection_code and bridge_socket_path
    • Bridge socket mounted as volume in container (Unix domain socket)
    • Injection code executed once on kernel startup (silent, no history)
    • Error handling for injection failures
    • Passed through SessionManager and JupyterBackend
  7. Network & Port Management

    • Host networking mode for ZMQ communication (network_mode="host")
    • Dynamic port allocation for 5 ZMQ channels (shell, iopub, stdin, control, hb)
    • Port availability checking before allocation
    • Port bindings tracked in ContainerConfig
    • Connection file mounted into container at /tmp/kernel-{id}.json
  8. Kernel Readiness

    • _wait_for_kernel_ready(): Polls shell port until kernel responds
    • Configurable timeout (default 30s) and poll interval (0.5s)
    • Replaces simple sleep with proper port connectivity check
    • Returns early when kernel is ready
  9. Dependencies

    • jupyter-client>=8.8.0 added to server dependencies
    • ipykernel removed from server (will be in container image)
    • pyzmq>=27.1.0 for ZMQ support
  10. Documentation

🚧 In Progress / TODO

  1. Restart Implementation

    • Use KernelManager instead of just BlockingKernelClient
    • Proper restart via KernelManager.restart_kernel()
    • Handle restart failures gracefully
  2. Container Image

    • Add ipykernel to mcp-forge/jupyter:latest image
    • Configure kernel to accept ZMQ connections
    • Test kernel startup in isolation
  3. Testing

    • Update tests in tests/execution/jupyter/test_kernel.py
    • Mock ZMQ connection and Jupyter messages
    • Mock container manager for unit tests
    • Integration tests with real containers
    • Test port conflict handling
    • Test connection file cleanup
  4. Error Handling

    • Port conflict resolution
    • Kernel crash detection and recovery
    • Connection timeout handling
    • Invalid connection file handling
  5. Performance

    • Connection pooling/reuse consideration
    • Batch message processing
    • Async execution option

Test Status

Current test results: 20 failed, 2 passed

Failure Reason: Tests attempt to start real Jupyter kernels, but implementation is missing:

  • Port mappings for ZMQ
  • Connection file mounting
  • Proper readiness check

Required Actions:

  1. Complete container configuration (ports, volumes)
  2. Update container image to include ipykernel
  3. Mock ZMQ connections in unit tests
  4. Create integration test suite with real containers

Known Issues

  1. Port Assignment: Currently using port: 0 (let ZMQ assign), but need to:

    • Get assigned ports after socket binding
    • Map container ports to host ports
    • Update connection info with actual ports
  2. Connection File: Generated on host, but needs to be:

    • Mounted into container at known path
    • Have correct host-accessible addresses
  3. Network Mode: Need to determine:

    • Host network mode (simpler, less isolated)
    • Bridge network with port mapping (more isolated, more complex)
  4. Restart Logic: restart_kernel() only updates timestamp:

    • Need KernelManager integration
    • Or implement shutdown + start approach

Next Steps

  1. Immediate (Core functionality):

    1. Update container image with ipykernel
    2. Test kernel startup with real container
    3. Mock tests for unit testing
    
  2. Short-term (Stability):

    4. Implement proper kernel restart
    5. Error handling improvements
    6. Port conflict handling
    
  3. Medium-term (Production-ready):

    7. Implement restart via KernelManager
    8. Integration test suite
    9. Error handling improvements
    10. Performance testing
    

Architecture Diagrams

Communication Flow

┌─────────────────────┐
│  MCP-Forge Server   │
│  (Host Process)     │
│                     │
│ ┌─────────────────┐ │
│ │ jupyter-client  │ │
│ │                 │ │
│ │ BlockingKernel  │ │
│ │ Client          │ │
│ └────────┬────────┘ │
│          │          │
│          │ ZMQ      │
│          │ (shell,  │
│          │  iopub,  │
│          │  stdin,  │
│          │  control,│
│          │  hb)     │
└──────────┼──────────┘
           │
           │ TCP ports
           │ (mapped)
           │
┌──────────┼──────────┐
│ Container│          │
│          ▼          │
│ ┌─────────────────┐ │
│ │   ipykernel     │ │
│ │                 │ │
│ │ Python REPL     │ │
│ │ + ZMQ Server    │ │
│ └─────────────────┘ │
│                     │
│ Connection file:    │
│ /tmp/kernel-X.json  │
└─────────────────────┘

Session-to-Kernel Mapping

Session A ──> Container A ──> Kernel A ──> Namespace A
Session B ──> Container B ──> Kernel B ──> Namespace B
Session C ──> Container C ──> Kernel C ──> Namespace C

Optional: Shared volume for file exchange
Container A ──┐
              ├──> /shared/session-group-1
Container B ──┘

Container C ──> /shared/session-group-2 (independent)

References

Commit History

  • 244a3e5 - Implement real Jupyter backend with jupyter-client
    • Replaced mock exec() with real Jupyter protocol
    • Added ZMQ communication
    • Kernel lifecycle management
    • Backed up old implementation

Old Implementation

The previous mock implementation using exec() is preserved in:

  • src/mcp_forge/execution/jupyter/kernel_old.py

This can be used for reference or comparison.