Jupyter MCP Server
v2.2.1Read, edit and run Jupyter notebooks over MCP. Cells are addressed by index within a notebook opened with use_notebook, and execution happens on the server, so a long computation keeps running after the session ends.
Add to your MCP client configuration:
Tools
clear_cell_output
destructiveidempotentClear the outputs and execution count of a single code cell in the currently activated notebook, without deleting the cell itself.
Body
cell_indexinteger | nullnullIndex of the code cell to clear (0-based). Omit when passing cell_id.
notebook_namestring | nullnullTarget this specific connected notebook instead of the currently activated one. Use when multiple clients share this server, to avoid racing the shared 'current notebook' pointer. Omit to use the currently activated notebook.
cell_idstring | nullnullAddress the cell by its notebook cell id instead of its index. An index is a position, and a position stops being true the moment anyone inserts a cell above it; an id does not. Every result says which id it acted on, so read a cell once and address it by id afterwards. Given both, the id wins.
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "clear_cell_output",
"arguments": {
"cell_index": null,
"notebook_name": null,
"cell_id": null
}
}
}const result = await client.callTool("clear_cell_output", {
"cell_index": null,
"notebook_name": null,
"cell_id": null
});result = await session.call_tool("clear_cell_output", arguments={
"cell_index": null,
"notebook_name": null,
"cell_id": null
}){
"cell_index": null,
"notebook_name": null,
"cell_id": null
}{
"kind": "string",
"result": null
}connect_to_jupyter
destructiveidempotentConnect to a Jupyter server dynamically with URL and token.
This tool allows you to connect to different Jupyter servers without needing to restart the MCP server or modify configuration files. Particularly useful when:
- Working with multiple Jupyter servers with different ports/tokens
- Jupyter server token changes dynamically
- Need to switch between different Jupyter instances
Example usage:
- "Connect to http://localhost:8888 with token abc123"
- "Connect to http://localhost:8889 without authentication"
Body
jupyter_urlstringrequiredJupyter server URL to connect to (e.g., 'http://localhost:8888')
jupyter_tokenstring | nullnullJupyter server authentication token
document_providerstringjupyterWhich backend holds the notebook documents
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "connect_to_jupyter",
"arguments": {
"jupyter_url": "string",
"jupyter_token": null,
"document_provider": "jupyter"
}
}
}const result = await client.callTool("connect_to_jupyter", {
"jupyter_url": "string",
"jupyter_token": null,
"document_provider": "jupyter"
});result = await session.call_tool("connect_to_jupyter", arguments={
"jupyter_url": "string",
"jupyter_token": null,
"document_provider": "jupyter"
}){
"jupyter_url": "string",
"jupyter_token": null,
"document_provider": "jupyter"
}{
"kind": "string",
"result": null
}delete_cell
destructiveDelete specific cells from the currently activated notebook and return the cell source of deleted cells (if include_source=True).
Body
cell_indicesArray<integer> | nullnullList of cell indices to delete (0-based). Omit when passing cell_ids_to_delete.
include_sourcebooleantrueWhether to include the source of deleted cells
notebook_namestring | nullnullTarget this specific connected notebook instead of the currently activated one. Use when multiple clients share this server, to avoid racing the shared 'current notebook' pointer. Omit to use the currently activated notebook.
cell_ids_to_deleteArray<string> | nullnullAddress the cells by their notebook cell ids instead of their indices. Safer for a multi-cell delete than indices, which shift as earlier cells go. Given both, the ids win; every id is checked before any cell is deleted, so a bad one fails the whole call rather than half-deleting the notebook.
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "delete_cell",
"arguments": {
"cell_indices": null,
"include_source": true,
"notebook_name": null,
"cell_ids_to_delete": null
}
}
}const result = await client.callTool("delete_cell", {
"cell_indices": null,
"include_source": true,
"notebook_name": null,
"cell_ids_to_delete": null
});result = await session.call_tool("delete_cell", arguments={
"cell_indices": null,
"include_source": true,
"notebook_name": null,
"cell_ids_to_delete": null
}){
"cell_indices": null,
"include_source": true,
"notebook_name": null,
"cell_ids_to_delete": null
}{
"kind": "string",
"result": null
}edit_cell_source
destructivePerform a surgical find-and-replace within a cell's source (like an editor's Edit tool).
Finds old_string in the cell and replaces it with new_string. Matching is literal
(not regex) and may span multiple lines. By default, old_string must appear exactly once;
set replace_all=True for multiple occurrences. Returns a diff of the changes made.
Prefer this over overwrite_cell_source for small, targeted edits — it is safer because unchanged parts of the cell are left untouched. Use read_cell first to see the current source and construct an accurate old_string.
Body
cell_indexinteger | nullnullIndex of the cell to edit (0-based). Omit when passing cell_id.
old_stringstringrequiredExact string to find in cell source
new_stringstringrequiredReplacement string
replace_allbooleanfalseReplace all occurrences (default: first only)
notebook_namestring | nullnullTarget this specific connected notebook instead of the currently activated one. Use when multiple clients share this server, to avoid racing the shared 'current notebook' pointer. Omit to use the currently activated notebook.
cell_idstring | nullnullAddress the cell by its notebook cell id instead of its index. An index is a position, and a position stops being true the moment anyone inserts a cell above it; an id does not. Every result says which id it acted on, so read a cell once and address it by id afterwards. Given both, the id wins.
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "edit_cell_source",
"arguments": {
"cell_index": null,
"old_string": "string",
"new_string": "string",
"replace_all": false,
"notebook_name": null,
"cell_id": null
}
}
}const result = await client.callTool("edit_cell_source", {
"cell_index": null,
"old_string": "string",
"new_string": "string",
"replace_all": false,
"notebook_name": null,
"cell_id": null
});result = await session.call_tool("edit_cell_source", arguments={
"cell_index": null,
"old_string": "string",
"new_string": "string",
"replace_all": false,
"notebook_name": null,
"cell_id": null
}){
"cell_index": null,
"old_string": "string",
"new_string": "string",
"replace_all": false,
"notebook_name": null,
"cell_id": null
}{
"kind": "string",
"result": null
}execute_cell
destructiveopen-worldExecute a cell from the currently activated notebook with timeout and return it's outputs
Body
cell_indexinteger | nullnullIndex of the cell to execute (0-based). Omit when passing cell_id.
timeoutinteger0Maximum seconds to wait for execution (0 = use config default)
streambooleantrueEnable streaming progress (including time indicator) updates for long-running cells
progress_intervalinteger5Seconds between progress updates (MCP keepalive + optional stream log)
cell_idstring | nullnullAddress the cell by its notebook cell id instead of its index. An index is a position, and a position stops being true the moment anyone inserts a cell above it; an id does not. Every result says which id it acted on, so read a cell once and address it by id afterwards. Given both, the id wins.
Returns
Cell or execution outputs, in order.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
outputsArray<any>The outputs in order: text as text, an image as its own object.
countinteger0How many outputs.
imagesinteger0How many of them are images.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "execute_cell",
"arguments": {
"cell_index": null,
"timeout": 0,
"stream": true,
"progress_interval": 5,
"cell_id": null
}
}
}const result = await client.callTool("execute_cell", {
"cell_index": null,
"timeout": 0,
"stream": true,
"progress_interval": 5,
"cell_id": null
});result = await session.call_tool("execute_cell", arguments={
"cell_index": null,
"timeout": 0,
"stream": true,
"progress_interval": 5,
"cell_id": null
}){
"cell_index": null,
"timeout": 0,
"stream": true,
"progress_interval": 5,
"cell_id": null
}{
"kind": "string",
"result": null,
"outputs": [],
"count": 0,
"images": 0
}execute_code
destructiveopen-worldExecute code directly in a kernel (not saved to notebook).
If use_sandbox selected an active sandbox, this tool executes on that
sandbox instead of a Jupyter kernel. This allows agents to switch between
kernel-backed and sandbox-backed execution using the same execute_code API.
Targets the current activated notebook's kernel by default. Pass kernel_id to execute in a specific kernel directly — including raw kernels with no notebook attached.
Recommended to use in following cases:
- Execute Jupyter magic commands(e.g.,
%timeit,%pip install xxx) - Performance profiling and debugging.
- View intermediate variable values(e.g.,
print(xxx),df.head()) - Temporary calculations and quick tests(e.g.,
np.mean(df['xxx'])) - Execute Shell commands in Jupyter server(e.g.,
!git xxx)
Under no circumstances should you use this tool to:
- Import new modules or perform variable assignments that affect subsequent Notebook execution
- Execute dangerous code that may harm the Jupyter server or the user's data without permission
Body
codestringrequiredCode to execute (supports magic commands with %, shell commands with !)
timeoutinteger30Maximum seconds to wait for execution (0 = use config default)
kernel_idstring | nullnullTarget an existing kernel by ID (e.g. a raw kernel with no notebook). If omitted, uses the current notebook's kernel.
progress_intervalinteger5Seconds between MCP progress keepalive updates during long-running execution
Returns
Cell or execution outputs, in order.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
outputsArray<any>The outputs in order: text as text, an image as its own object.
countinteger0How many outputs.
imagesinteger0How many of them are images.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "execute_code",
"arguments": {
"code": "string",
"timeout": 30,
"kernel_id": null,
"progress_interval": 5
}
}
}const result = await client.callTool("execute_code", {
"code": "string",
"timeout": 30,
"kernel_id": null,
"progress_interval": 5
});result = await session.call_tool("execute_code", arguments={
"code": "string",
"timeout": 30,
"kernel_id": null,
"progress_interval": 5
}){
"code": "string",
"timeout": 30,
"kernel_id": null,
"progress_interval": 5
}{
"kind": "string",
"result": null,
"outputs": [],
"count": 0,
"images": 0
}insert_cell
destructiveInsert a cell to specified position from the currently activated notebook.
Body
cell_indexinteger>= -1requiredTarget index for insertion (0-based), use -1 to append at end
cell_typestringcodemarkdownrawrequiredType of cell to insert
cell_sourcestringrequiredSource content for the cell
notebook_namestring | nullnullTarget this specific connected notebook instead of the currently activated one. Use when multiple clients share this server, to avoid racing the shared 'current notebook' pointer. Omit to use the currently activated notebook.
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "insert_cell",
"arguments": {
"cell_index": -1,
"cell_type": "code",
"cell_source": "string",
"notebook_name": null
}
}
}const result = await client.callTool("insert_cell", {
"cell_index": -1,
"cell_type": "code",
"cell_source": "string",
"notebook_name": null
});result = await session.call_tool("insert_cell", arguments={
"cell_index": -1,
"cell_type": "code",
"cell_source": "string",
"notebook_name": null
}){
"cell_index": -1,
"cell_type": "code",
"cell_source": "string",
"notebook_name": null
}{
"kind": "string",
"result": null
}insert_execute_code_cell
destructiveopen-worldInsert a cell at specified index from the currently activated notebook and then execute it with timeout and return it's outputs It is a shortcut tool for insert_cell and execute_cell tools, recommended to use if you want to insert a cell and execute it at the same time
Parameters
cell_indexinteger>= -1requiredargumentIndex of the cell to insert and execute (0-based)
cell_sourcestringrequiredargumentCode source for the cell
timeoutinteger0argumentMaximum seconds to wait for execution (0 = use config default)
streambooleantrueargumentEnable streaming progress (including time indicator) updates for long-running cells
progress_intervalinteger5argumentSeconds between progress updates (MCP keepalive + optional stream log)
Returns
Cell or execution outputs, in order.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
outputsArray<any>The outputs in order: text as text, an image as its own object.
countinteger0How many outputs.
imagesinteger0How many of them are images.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "insert_execute_code_cell",
"arguments": {
"cell_index": -1,
"cell_source": "string",
"timeout": 0,
"stream": true,
"progress_interval": 5
}
}
}const result = await client.callTool("insert_execute_code_cell", {
"cell_index": -1,
"cell_source": "string",
"timeout": 0,
"stream": true,
"progress_interval": 5
});result = await session.call_tool("insert_execute_code_cell", arguments={
"cell_index": -1,
"cell_source": "string",
"timeout": 0,
"stream": true,
"progress_interval": 5
}){
"kind": "string",
"result": null,
"outputs": [],
"count": 0,
"images": 0
}launch_sandbox
destructiveopen-worldLaunch a code sandbox that can be used instead of Jupyter kernels.
After launch, call use_sandbox to make execute_code run on this sandbox (as an alternative to notebook-bound kernel execution). Works in both MCP_SERVER and JUPYTER_SERVER modes.
Body
sandbox_namestringrequiredUnique sandbox identifier used by list/use/terminate tools
variantstring | nullnullSandbox variant to launch. If omitted, defaults to configured SANDBOX_VARIANT when it is not jupyter-server; otherwise falls back to eval.
timeoutinteger>= 160Default execution timeout in seconds for this sandbox
environmentstring | nullnullOptional sandbox environment name (common for datalayer/modal variants). On the datalayer variant this is a platform environment by name, such as ai-agents-env, or an environment somebody built, written 'account/name' — the account being a person's handle or an organization's.
environment_versionstring | integer | nullnullWhich version of an 'account/name' environment to launch: its number, such as 3, or a version uid. Leave it unset to get the version its owner promoted, which is what you want unless you were asked for a particular one. Only the datalayer variant has versions; naming one for any other variant is refused rather than quietly ignored.
gpustring | nullnullOptional GPU flavor / accelerator. Only coreweave, datalayer, daytona, kaggle and modal have a GPU; asking one of the others (e2b, cloudflare, docker, eval, google-colab, jupyter-server, monty) for a GPU is refused rather than quietly run on a CPU, so leave this unset for them. Examples: modal/datalayer T4, A10G, A100, H100; daytona H100, H200, RTX-4090; coreweave H100; kaggle NvidiaTeslaT4, NvidiaTeslaP100, or the aliases T4/P100.
server_urlstring | nullnullCode Sandbox proxy URL when using the google-colab or kaggle variant
kernel_idstring | nullnullKernel ID when using the google-colab or kaggle variant
proxy_tokenstring | nullnullGoogle Colab code sandbox proxy token when using google-colab variant
channels_urlstring | nullnullNotebook session WebSocket channels URL to derive server_url/kernel_id (google-colab or kaggle variant)
tokenstring | nullnullKaggle API token for the kaggle variant (falls back to KAGGLE_API_TOKEN)
python_versionstring | nullnullModal Python version override (e.g. 3.12). Only used for modal variant.
snapshot_namestring | nullnullStart from a saved snapshot instead of an empty sandbox, restoring the state it was taken in. Only the datalayer variant has snapshots; naming one for any other variant is refused rather than quietly started empty.
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "launch_sandbox",
"arguments": {
"sandbox_name": "string",
"variant": null,
"timeout": 60,
"environment": null,
"environment_version": null,
"gpu": null,
"server_url": null,
"kernel_id": null,
"proxy_token": null,
"channels_url": null,
"token": null,
"python_version": null,
"snapshot_name": null
}
}
}const result = await client.callTool("launch_sandbox", {
"sandbox_name": "string",
"variant": null,
"timeout": 60,
"environment": null,
"environment_version": null,
"gpu": null,
"server_url": null,
"kernel_id": null,
"proxy_token": null,
"channels_url": null,
"token": null,
"python_version": null,
"snapshot_name": null
});result = await session.call_tool("launch_sandbox", arguments={
"sandbox_name": "string",
"variant": null,
"timeout": 60,
"environment": null,
"environment_version": null,
"gpu": null,
"server_url": null,
"kernel_id": null,
"proxy_token": null,
"channels_url": null,
"token": null,
"python_version": null,
"snapshot_name": null
}){
"sandbox_name": "string",
"variant": null,
"timeout": 60,
"environment": null,
"environment_version": null,
"gpu": null,
"server_url": null,
"kernel_id": null,
"proxy_token": null,
"channels_url": null,
"token": null,
"python_version": null,
"snapshot_name": null
}{
"kind": "string",
"result": null
}list_files
read-onlyidempotentList all files and directories recursively in the Jupyter server's file system. Used to explore the file system structure of the Jupyter server or to find specific files or directories.
Parameters
pathstringargumentThe starting path to list from (empty string means root directory)
max_depthinteger[0, 3]1argumentMaximum depth to recurse into subdirectories
start_indexinteger>= 00argumentStarting index for pagination (0-based)
limitinteger>= 025argumentMaximum number of items to return (0 means no limit)
patternstringargumentGlob pattern to filter file paths
Returns
A listing that also comes back as rows keyed by its header.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
columnsArray<string>The header, in order.
itemsArray<object>One object per row, keyed by the header.
countinteger0How many rows.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "list_files",
"arguments": {
"path": "",
"max_depth": 1,
"start_index": 0,
"limit": 25,
"pattern": ""
}
}
}const result = await client.callTool("list_files", {
"path": "",
"max_depth": 1,
"start_index": 0,
"limit": 25,
"pattern": ""
});result = await session.call_tool("list_files", arguments={
"path": "",
"max_depth": 1,
"start_index": 0,
"limit": 25,
"pattern": ""
}){
"kind": "string",
"result": null,
"columns": [
"string"
],
"items": [
{}
],
"count": 0
}list_kernels
read-onlyidempotentList all available kernels in the Jupyter server.
This tool shows all running and available kernel sessions on the Jupyter server, including their IDs, names, states, connection information, and kernel specifications. Useful for monitoring kernel resources and identifying specific kernels for connection.
Returns
A listing that also comes back as rows keyed by its header.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
columnsArray<string>The header, in order.
itemsArray<object>One object per row, keyed by the header.
countinteger0How many rows.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "list_kernels",
"arguments": {}
}
}const result = await client.callTool("list_kernels", {});result = await session.call_tool("list_kernels", arguments={}){
"kind": "string",
"result": null,
"columns": [
"string"
],
"items": [
{}
],
"count": 0
}list_notebooks
read-onlyidempotentList all notebooks that have been used via use_notebook tool
Returns
A listing that also comes back as rows keyed by its header.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
columnsArray<string>The header, in order.
itemsArray<object>One object per row, keyed by the header.
countinteger0How many rows.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "list_notebooks",
"arguments": {}
}
}const result = await client.callTool("list_notebooks", {});result = await session.call_tool("list_notebooks", arguments={}){
"kind": "string",
"result": null,
"columns": [
"string"
],
"items": [
{}
],
"count": 0
}list_sandboxes
read-onlyidempotentList launched code sandboxes that can be used as alternatives to kernels.
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "list_sandboxes",
"arguments": {}
}
}const result = await client.callTool("list_sandboxes", {});result = await session.call_tool("list_sandboxes", arguments={}){
"kind": "string",
"result": null
}move_cell
destructiveMove a cell from source_index to target_index within the currently activated notebook.
The cell is removed from source_index and placed at target_index. Cells in between shift to fill the gap. The cell's type, source, and outputs are preserved. Example: in a notebook [A, B, C, D], move_cell(1, 3) produces [A, C, D, B].
Use this tool instead of manually deleting and re-inserting a cell — it is atomic and preserves cell metadata. Use read_notebook first to see cell indices if needed.
Body
source_indexinteger | nullnullIndex of the cell to move (0-based). Omit when passing source_cell_id.
target_indexinteger | nullnullDestination index where the cell will end up (0-based). Omit when passing target_cell_id.
notebook_namestring | nullnullTarget this specific connected notebook instead of the currently activated one. Use when multiple clients share this server, to avoid racing the shared 'current notebook' pointer. Omit to use the currently activated notebook.
source_cell_idstring | nullnullAddress the cell to move by its id rather than its index.
target_cell_idstring | nullnullPut the moved cell where this cell is now, addressed by id rather than by an index that the move itself will shift.
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "move_cell",
"arguments": {
"source_index": null,
"target_index": null,
"notebook_name": null,
"source_cell_id": null,
"target_cell_id": null
}
}
}const result = await client.callTool("move_cell", {
"source_index": null,
"target_index": null,
"notebook_name": null,
"source_cell_id": null,
"target_cell_id": null
});result = await session.call_tool("move_cell", arguments={
"source_index": null,
"target_index": null,
"notebook_name": null,
"source_cell_id": null,
"target_cell_id": null
}){
"source_index": null,
"target_index": null,
"notebook_name": null,
"source_cell_id": null,
"target_cell_id": null
}{
"kind": "string",
"result": null
}overwrite_cell_source
destructiveidempotentReplace the entire source of a cell in the currently activated notebook. Returns a diff showing the changes made.
Use this when rewriting a cell completely. For small, targeted changes, prefer edit_cell_source instead — it is safer for partial edits.
Body
cell_indexinteger | nullnullIndex of the cell to overwrite (0-based). Omit when passing cell_id.
cell_sourcestringrequiredNew complete cell source
notebook_namestring | nullnullTarget this specific connected notebook instead of the currently activated one. Use when multiple clients share this server, to avoid racing the shared 'current notebook' pointer. Omit to use the currently activated notebook.
cell_idstring | nullnullAddress the cell by its notebook cell id instead of its index. An index is a position, and a position stops being true the moment anyone inserts a cell above it; an id does not. Every result says which id it acted on, so read a cell once and address it by id afterwards. Given both, the id wins.
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "overwrite_cell_source",
"arguments": {
"cell_index": null,
"cell_source": "string",
"notebook_name": null,
"cell_id": null
}
}
}const result = await client.callTool("overwrite_cell_source", {
"cell_index": null,
"cell_source": "string",
"notebook_name": null,
"cell_id": null
});result = await session.call_tool("overwrite_cell_source", arguments={
"cell_index": null,
"cell_source": "string",
"notebook_name": null,
"cell_id": null
}){
"cell_index": null,
"cell_source": "string",
"notebook_name": null,
"cell_id": null
}{
"kind": "string",
"result": null
}read_cell
read-onlyidempotentRead a cell as readable text entries.
Includes metadata and source, plus optional formatted output text rather than raw nbformat objects.
Body
cell_indexinteger | nullnullIndex of the cell to read (0-based). Omit when passing cell_id.
include_outputsbooleantrueInclude outputs in the response (only for code cells)
notebook_namestring | nullnullTarget this specific connected notebook instead of the currently activated one. Use when multiple clients share this server, to avoid racing the shared 'current notebook' pointer. Omit to use the currently activated notebook.
cell_idstring | nullnullAddress the cell by its notebook cell id instead of its index. An index is a position, and a position stops being true the moment anyone inserts a cell above it; an id does not. Every result says which id it acted on, so read a cell once and address it by id afterwards. Given both, the id wins.
Returns
Cell or execution outputs, in order.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
outputsArray<any>The outputs in order: text as text, an image as its own object.
countinteger0How many outputs.
imagesinteger0How many of them are images.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "read_cell",
"arguments": {
"cell_index": null,
"include_outputs": true,
"notebook_name": null,
"cell_id": null
}
}
}const result = await client.callTool("read_cell", {
"cell_index": null,
"include_outputs": true,
"notebook_name": null,
"cell_id": null
});result = await session.call_tool("read_cell", arguments={
"cell_index": null,
"include_outputs": true,
"notebook_name": null,
"cell_id": null
}){
"cell_index": null,
"include_outputs": true,
"notebook_name": null,
"cell_id": null
}{
"kind": "string",
"result": null,
"outputs": [],
"count": 0,
"images": 0
}read_notebook
read-onlyidempotentRead a notebook and return index, source content, type, execution count of each cell.
Using brief format to get a quick overview of the notebook structure and it's useful for locating specific cells for operations like delete or insert. Using detailed format to get detailed information of the notebook and it's useful for debugging and analysis.
It is recommended to use brief format with larger limit to get a overview of the notebook structure, then use detailed format with exact index and limit to get the detailed information of some specific cells.
Parameters
notebook_namestringrequiredargumentNotebook identifier to read
response_formatstringbriefdetailedbriefargumentResponse format: 'brief' will return first line and lines number, 'detailed' will return full cell source
start_indexinteger>= 00argumentStarting index for pagination (0-based)
limitinteger>= 020argumentMaximum number of items to return (0 means no limit)
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "read_notebook",
"arguments": {
"notebook_name": "string",
"response_format": "brief",
"start_index": 0,
"limit": 20
}
}
}const result = await client.callTool("read_notebook", {
"notebook_name": "string",
"response_format": "brief",
"start_index": 0,
"limit": 20
});result = await session.call_tool("read_notebook", arguments={
"notebook_name": "string",
"response_format": "brief",
"start_index": 0,
"limit": 20
}){
"kind": "string",
"result": null
}restart_notebook
destructiveRestart the kernel for a specific notebook.
Parameters
notebook_namestringrequiredargumentNotebook identifier to restart
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "restart_notebook",
"arguments": {
"notebook_name": "string"
}
}
}const result = await client.callTool("restart_notebook", {
"notebook_name": "string"
});result = await session.call_tool("restart_notebook", arguments={
"notebook_name": "string"
}){
"kind": "string",
"result": null
}terminate_sandbox
destructiveidempotentTerminate a launched code sandbox.
Parameters
sandbox_namestringrequiredargumentSandbox name to terminate and unregister
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "terminate_sandbox",
"arguments": {
"sandbox_name": "string"
}
}
}const result = await client.callTool("terminate_sandbox", {
"sandbox_name": "string"
});result = await session.call_tool("terminate_sandbox", arguments={
"sandbox_name": "string"
}){
"kind": "string",
"result": null
}unuse_notebook
destructiveidempotentUnuse from a specific notebook and release its resources.
Parameters
notebook_namestringrequiredargumentNotebook identifier to disconnect
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "unuse_notebook",
"arguments": {
"notebook_name": "string"
}
}
}const result = await client.callTool("unuse_notebook", {
"notebook_name": "string"
});result = await session.call_tool("unuse_notebook", arguments={
"notebook_name": "string"
}){
"kind": "string",
"result": null
}use_notebook
destructiveidempotentUse a notebook and activate it for following cell operations. All cell operations will be performed on the currently activated notebook. Activate new notebook will deactivate the previously activated notebook. Reactivate previously activated notebook using same notebook_name and notebook_path.
Parameters
notebook_namestringrequiredargumentUnique identifier for the notebook
notebook_pathstringrequiredargumentPath to the notebook file, relative to the Jupyter server root (e.g. 'notebook.ipynb')
modestringconnectcreateconnectargumentNotebook operation mode: 'connect' to connect to existing and activate it, 'create' to create new and activate it
kernel_idstringnullargumentId of an existing kernel (or sandbox, for a non-Jupyter sandbox variant) to attach the notebook to. A new one is created if skipped.
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "use_notebook",
"arguments": {
"notebook_name": "string",
"notebook_path": "string",
"mode": "connect",
"kernel_id": null
}
}
}const result = await client.callTool("use_notebook", {
"notebook_name": "string",
"notebook_path": "string",
"mode": "connect",
"kernel_id": null
});result = await session.call_tool("use_notebook", arguments={
"notebook_name": "string",
"notebook_path": "string",
"mode": "connect",
"kernel_id": null
}){
"kind": "string",
"result": null
}use_sandbox
destructiveidempotentSelect which launched sandbox execute_code should use instead of kernels.
Body
sandbox_namestring | nullnullSandbox name to activate for execute_code. Pass null/empty to disable sandbox routing and return to Jupyter kernels.
Returns
What every tool of this server answers with.
Declared so the shape is advertised rather than merely produced. A tool that returns structure without saying what it will return leaves a client nothing to validate against and the generated reference nothing to show — the call works and the contract is invisible, which is the worst of both.
Extra fields are allowed on purpose. A tool that already answers with a
mapping keeps its own keys (see :func:_default_shape), and those are the
interesting part of its answer; forbidding them would mean either
flattening every tool into one shape or declaring nothing at all.
kindstringrequiredWhat this result is — 'cell.read', 'notebooks.list' and so on. Lets a client tell one answer from another without matching prose.
resultanynullThe answer itself: a message, the rows of a listing, or the outputs of an execution in order.
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "use_sandbox",
"arguments": {
"sandbox_name": null
}
}
}const result = await client.callTool("use_sandbox", {
"sandbox_name": null
});result = await session.call_tool("use_sandbox", arguments={
"sandbox_name": null
}){
"sandbox_name": null
}{
"kind": "string",
"result": null
}Resources
capabilities_resource
What this server can do, and where each answer came from.
A server does things a client cannot see and did not ask for — replacing a dead kernel with an empty one is the clearest case. Reading this is how a client finds out which of those are on, and an operator finds out why a capability is on, which is the first question asked when one surprises somebody.
A resource as well as a server/discover field, because a client may
want to re-read it without re-discovering the server, and because a
person can open a resource and look.
Returns
Returns MCP content array (text, image, or embedded resource).
{
"jsonrpc": "2.0",
"method": "resources/read",
"params": {
"uri": "capabilities://"
}
}const result = await client.readResource("capabilities://");result = await session.read_resource("capabilities://"){
"content": [
{
"type": "text",
"text": "..."
}
]
}notebook
A notebook in use, as nbformat JSON. Cells and outputs are resources of their own.
Parameters
namestringrequiredpathReturns
Returns MCP content array (text, image, or embedded resource).
{
"jsonrpc": "2.0",
"method": "resources/read",
"params": {
"uri": "notebook://{name}"
}
}const result = await client.readResource("notebook://{name}");result = await session.read_resource("notebook://{name}"){
"content": [
{
"type": "text",
"text": "..."
}
]
}notebook-cell
One cell by its nbformat id, with its outputs listed rather than inlined.
Parameters
namestringrequiredpathcell_idstringrequiredpathReturns
Returns MCP content array (text, image, or embedded resource).
{
"jsonrpc": "2.0",
"method": "resources/read",
"params": {
"uri": "notebook://{name}/cells/{cell_id}"
}
}const result = await client.readResource("notebook://{name}/cells/{cell_id}");result = await session.read_resource("notebook://{name}/cells/{cell_id}"){
"content": [
{
"type": "text",
"text": "..."
}
]
}notebook-cell-output
One output of one cell, with its own MIME type. Read it only if you need the bytes.
Parameters
namestringrequiredpathcell_idstringrequiredpathindexstringrequiredpathReturns
Returns MCP content array (text, image, or embedded resource).
{
"jsonrpc": "2.0",
"method": "resources/read",
"params": {
"uri": "notebook://{name}/cells/{cell_id}/outputs/{index}"
}
}const result = await client.readResource("notebook://{name}/cells/{cell_id}/outputs/{index}");result = await session.read_resource("notebook://{name}/cells/{cell_id}/outputs/{index}"){
"content": [
{
"type": "text",
"text": "..."
}
]
}Prompts
jupyter_cite
Like @ or # in Coding IDE or CLI, cite specific cells from specified notebook and insert them into the prompt.
Parameters
promptstringrequiredargumentUser prompt for the cited cells
cell_indicesstringrequiredargumentCell indices to cite (0-based),supporting flexible range format, e.g., '0,1,2', '0-2' or '0-2,4'
notebook_namestringargumentName of the notebook to cite cells from, default (empty) to current activated notebook
Returns
Returns MCP content array (text, image, or embedded resource).
{
"jsonrpc": "2.0",
"method": "prompts/get",
"params": {
"name": "jupyter_cite",
"arguments": {
"prompt": "<prompt>",
"cell_indices": "<cell_indices>",
"notebook_name": "<notebook_name>"
}
}
}const result = await client.getPrompt("jupyter_cite", {
prompt: "<prompt>",
cell_indices: "<cell_indices>",
notebook_name: "<notebook_name>",
});result = await session.get_prompt("jupyter_cite", arguments={
"prompt": "<prompt>",
"cell_indices": "<cell_indices>",
"notebook_name": "<notebook_name>",
}){
"content": [
{
"type": "text",
"text": "..."
}
]
}