Jupyter_ai mcp server port conflict

We use jupyterhub version 5.3 with single-user-server as jupyter server. After installing jupyter_ai we have run into an issue where only one user can login to jupyterhub at a time because the jupyter server mcp server starts up and binds to port 3001 and so other jupyter servers cannot start up due to port conflict. This is the error in the log: “ERROR: [Errno 98] error while attempting to bind on address (‘127.0.0.1’, 3001): address already in use”. When the first user’s server is spawned we see this in the log: Starting MCP server ‘Jupyter MCP transport.py:299
Server’ with transport ‘http’ on http://localhost:3001/mcp

Any ideas on how we can avoid this issue by having a single jupyter-server-mcp server for all users instead of each user having their own and running into this port conflict?

We are just trying jupyter_ai in a dev environment but have run into this issue. Any help will be greatly appreciated. thanks!

The port is governed by c.MCPExtensionApp.mcp_port in your jupyter_server_config.

You can either try to claim an available port and assign that, or if it’s a system you reasonably control, you can pick a block of ports and assign within that using offsets, for example on a shared system use the userid to give each user a unique port:

# jupyter_server_config.py
import os

mcp_port_offset = 10_000
c.MCPExtensionApp.mcp_port = mcp_port_offset + os.getuid()

Running one mcp server for all wouldn’t really work, because it would mean that all users would be taking operations against the same mcp target (e.g. editing notebooks on the same user’s server instead of their own).

Thanks for your response. This sounds like a good idea but I’m struggling a bit to implement as I don’t see a juputer_server_config.py in our system. Perhaps we use all default; I will check with colleagues. We use the Single-User Notebook Server.

Trying to generate a new config file (as root) gave the following with no file that I could find:

>>jupyter server --generate-config
Writing default config to: {config_file!r}

For JupyterHub, you’ll want to put it in a shared location, like /usr/local/etc/jupyter/jupyter_server_config.py

You can see the list of directories searched for this file in the config category with jupyter --paths, which will look something like:

config:
    /Users/you/.jupyter
    /Users/you/.local/etc/jupyter
    /opt/conda/etc/jupyter
    /usr/local/etc/jupyter
    /etc/jupyter

depending on your user and Python install location. /etc/jupyter and /usr/local/etc/jupyter will always be there.

Thank you! Placing the generated jupyter_server_config.py file in /usr/local/etc/jupyter worked and adding logic to find and allocate an available tcp port to the mcp extension works!

However, as we use batchspawner to start a Jupyter Server (via SingleUser server) on remote machines as well, I would like to have this config file on a shared file system so I don’t have to deploy it onto all nodes of the cluster. Is there a way to tell the SingleUser server launched via batchspawner to use this shared location as a config path location for Jupyter server?

This below is the setting in the jupyterhub config file that sets the spawner command. I’m thinking adding “–config=” to this command might do the trick? I’m fairly new to jupyterhub so not sure how things work :slight_smile:

c.Spawner.cmd = [‘/usr/local/bin/jupyterhub-singleuser’]

thanks again!

Is there a way to tell the SingleUser server launched via batchspawner to use this shared location as a config path location for Jupyter server?

Yes. You can check jupyter --paths to see the config directories Jupyter Server searches by default, in case any of those are on the shared filesystem. You can also set the JUPYTER_CONFIG_DIR env variable to point to your location if it’s not already on the list.

Adding --config=/path/to/jupyter_server_config.py to c.Spawner.args in your jupyterhub configuration also works:

c.Spawner.args = ["--config=/path/to/jupyter_server_config.py"]

but will only affect the server itself, whereas JUPYTER_CONFIG_DIR will affect all Jupyter-related subprocess, which may be useful if you have any other config you want to apply for all users by default.

Just to update you:

While setting the c.Spawner.args with the “–config” option in jupyterhub’s config file worked, the other option of setting JUPYTER_CONFIG_DIR to the alternate desired path to the jupyter server config file in c.Spawner.environment did not work. First, I had to set the value of JUPYTER_CONFIG_DIR to the full file path including the config file name (for example ‘JUPYTER_CONFIG_DIR’:‘/my/shared/folder/jupyter_server_config.py’) not just the folder name (otherwise the server would not even start up as it tries to open a folder as opposed to a file), but even then it is not actually using this config file (i.e. my mcp port change is not picked up).

I see the following Warnings in jupyterhub log that might give a clue:

[W 2026-05-08 15:04:53.241 LabApp] ‘extra_template_paths’ was found in both NotebookApp and ServerApp. This is likely a recent change. This config will only be set in Noteb
ookApp. Please check if you should also config these traits in ServerApp for your purpose.
[W 2026-05-08 15:04:53.244 JupyterNotebookApp] ‘extra_template_paths’ was found in both NotebookApp and ServerApp. This is likely a recent change. This config will only be
set in NotebookApp. Please check if you should also config these traits in ServerApp for your purpose.

For now, I am going to proceed with the spawner.args option.

thanks!