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.
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_runandjupyter_terminal_writeappropriately. These tools can be used to run arbitrary shell commands on the JupyterLab host, which is a security risk.
Recommended Servers
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.
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.
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.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
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.
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.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.
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.