Jupyter Terminal MCP

Jupyter Terminal MCP

Enables an agent to run shell commands through a JupyterLab/JupyterHub-hosted terminal, using REST API and WebSocket via Selenium for session handling, when SSH is not available.

Category
Visit Server

README

Jupyter Terminal MCP

This MCP server lets an agent run commands through a JupyterLab/JupyterHub-hosted terminal when SSH is not available. Originally designed for man-in-the-loop debugging of NPU kernels on AMD's AUP Learning Cloud, it talks to a Jupyter Server terminal REST API and websocket endpoint using a Selenium WebDriver to keep a valid session and perform the requests. You need access to the JupyterLab machine from the system hosting the MCP server. You can also use this server without a browser, if you have a valid Jupyter token and the server is not behind Cloudflare or other login challenges.

[!WARNING] You must have permission from the system administrator to use the JupyterLab environment for research/development. Do not use this software to break rules or bypass security measures. The author is not responsible for any misuse of this software. Not affiliated with Jupyter or any other org. Always verify AI-generated commands before accepting and executing them.

Install

UV_CACHE_DIR=.uv-cache uv sync

Configure

Keep your token in environment variables or your agent's MCP config JSON.

export JUPYTER_URL="https://myjupyterserver.example.com/user/my-user/lab"
export JUPYTER_TOKEN="<your Jupyter token>" # only supported in non-browser mode
export JUPYTER_USERNAME="<your Jupyter username>"

Optional settings:

export JUPYTER_CONNECT_TIMEOUT="10"
export JUPYTER_VERIFY_TLS="true"
export JUPYTER_WEBSOCKET_RETRIES="3"
export JUPYTER_WEBSOCKET_RETRY_DELAY="1"

JUPYTER_URL may point at /lab; the server automatically converts it to the Jupyter API base URL. The server opens the Lab URL, follows the Hub login/OAuth flow, and submits JUPYTER_USERNAME plus JUPYTER_TOKEN as the login credentials. If JUPYTER_USERNAME is omitted, it is derived from /user/<name>/ in JUPYTER_URL.

Websocket connection failures with transient statuses such as 503 Service Unavailable are retried. Before each retry, the server refreshes the JupyterHub session cookies.

In my experience, custom terminal names can sometimes remain listed after their websocket route has gone stale. Leaving JUPYTER_TERMINAL_NAME unset is usually more robust: the server reuses existing numeric terminals first, starting from the lowest name (1, then 2, and so on). If a selected terminal returns a 502/503/504 websocket handshake, the server tries the next existing numeric terminal before creating a fresh unnamed terminal.

Browser-backed mode does not require JUPYTER_TOKEN; the logged-in browser session supplies the cookies. Useful browser/socket settings:

export JUPYTER_MCP_SOCKET_HOST="127.0.0.1"
export JUPYTER_MCP_SOCKET_PORT="8765"
export CHROMEDRIVER="/path/to/chromedriver"                  # optional if chromedriver is already on PATH
export JUPYTER_CHROME_BINARY="/path/to/Google Chrome"         # optional
export JUPYTER_CHROME_USER_DATA_DIR="/path/to/user-data-dir"  # optional; reuse a Chrome user data dir
export JUPYTER_CHROME_PROFILE_DIRECTORY="Default"             # optional; profile inside the user data dir
export JUPYTER_BROWSER_KEEP_OPEN="true"                       # optional; leave Chrome open when the daemon exits

Run

For a quick local config check:

UV_CACHE_DIR=.uv-cache uv run jupyter-terminal-mcp --check-config

For browser-backed mode, run the browser daemon in a regular terminal first:

export JUPYTER_URL="https://myjupyterserver.example.com/user/my-user/lab"
UV_CACHE_DIR=.uv-cache uv run jupyter-terminal-mcp --browser-server

Chrome opens. Complete any login procedure, Cloudflare challenge etc. manually, wait until JupyterLab is ready, then press Enter in the daemon terminal. The daemon listens for MCP JSON-RPC messages on 127.0.0.1:8765 by default.

Then configure MCP clients to launch the stdio socket proxy:

UV_CACHE_DIR=.uv-cache uv run jupyter-terminal-mcp --socket-client

Example browser-backed MCP configuration:

{
  "mcpServers": {
    "jupyter-terminal": {
      "command": "uv",
      "args": [
        "--directory",
        "~/Projects/jupyter-terminal-mcp",
        "run",
        "jupyter-terminal-mcp",
        "--socket-client"
      ],
      "env": {
        "UV_CACHE_DIR": "~/Projects/jupyter-terminal-mcp/.uv-cache",
        "JUPYTER_MCP_SOCKET_HOST": "127.0.0.1",
        "JUPYTER_MCP_SOCKET_PORT": "8765"
      }
    }
  }
}

In non-browser-backed mode, clients should launch:

UV_CACHE_DIR=.uv-cache uv run jupyter-terminal-mcp

Example non-browser-backed MCP configuration:

{
  "mcpServers": {
    "jupyter-terminal": {
      "command": "uv",
      "args": [
        "--directory",
        "~/Projects/jupyter-terminal-mcp",
        "run",
        "jupyter-terminal-mcp"
      ],
      "env": {
        "UV_CACHE_DIR": "~/Projects/jupyter-terminal-mcp/.uv-cache",
        "JUPYTER_URL": "https://myjupyterserver.example.com/user/my-user/lab",
        "JUPYTER_TOKEN": "<your Jupyter token>",
        "JUPYTER_USERNAME": "my-user"
      }
    }
  }
}

Tools

Your coding agent can then use the following tools:

Tool Description
jupyter_terminal_run Run a shell command and return JSON with output, exit_code, terminal_name, timed_out, and elapsed time.
jupyter_terminal_write Send raw text to the terminal for interactive prompts.
jupyter_terminal_list List Jupyter terminals visible to the server.

jupyter_terminal_run wraps each command with unique begin/end markers so the agent receives only the command's output and exit status. If a command times out, the server sends ^C to the terminal and returns the output collected so far.

[!WARNING] Make sure to gate access to jupyter_terminal_run and jupyter_terminal_write appropriately. These tools can be used to run arbitrary shell commands on the JupyterLab host, which is a security risk.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured