Skip to content

Commit 88f914d

Browse files
rename execute_ipython to execute_code (#134)
1 parent 362597b commit 88f914d

File tree

6 files changed

+63
-65
lines changed

6 files changed

+63
-65
lines changed

jupyter_mcp_server/server.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
# Cell Execution
5454
ExecuteCellTool,
5555
# Other Tools
56-
ExecuteIpythonTool,
56+
ExecuteCodeTool,
5757
ListFilesTool,
5858
ListKernelsTool,
5959
)
@@ -470,23 +470,22 @@ async def delete_cell(
470470

471471

472472
@mcp.tool()
473-
async def execute_ipython(
474-
code: Annotated[str, Field(description="IPython code to execute (supports magic commands, shell commands with !, and Python code)")],
475-
timeout: Annotated[int, Field(description="Execution timeout in seconds (default: 60s)")] = 60,
473+
async def execute_code(
474+
code: Annotated[str, Field(description="Code to execute (supports magic commands with %, shell commands with !)")],
475+
timeout: Annotated[int, Field(description="Execution timeout in seconds",le=60)] = 30,
476476
) -> Annotated[list[str | ImageContent], Field(description="List of outputs from the executed code")]:
477-
"""Execute IPython code directly in the kernel on the current active notebook.
478-
479-
This powerful tool supports:
480-
1. Magic commands (e.g., %timeit, %who, %load, %run, %matplotlib)
481-
2. Shell commands (e.g., !pip install, !ls, !cat)
482-
3. Python code (e.g., print(df.head()), df.info())
483-
484-
Use cases:
485-
- Performance profiling and debugging
486-
- Environment exploration and package management
487-
- Variable inspection and data analysis
488-
- File system operations on Jupyter server
489-
- Temporary calculations and quick tests
477+
"""Execute code directly in the kernel (not saved to notebook) on the current activated notebook.
478+
479+
Recommended to use in following cases:
480+
1. Execute Jupyter magic commands(e.g., `%timeit`, `%pip install xxx`)
481+
2. Performance profiling and debugging.
482+
3. View intermediate variable values(e.g., `print(xxx)`, `df.head()`)
483+
4. Temporary calculations and quick tests(e.g., `np.mean(df['xxx'])`)
484+
5. Execute Shell commands in Jupyter server(e.g., `!git xxx`)
485+
486+
Under no circumstances should you use this tool to:
487+
1. Import new modules or perform variable assignments that affect subsequent Notebook execution
488+
2. Execute dangerous code that may harm the Jupyter server or the user's data without permission
490489
"""
491490
# Get kernel_id for JUPYTER_SERVER mode
492491
# Let the tool handle getting kernel_id via get_current_notebook_context()
@@ -498,7 +497,7 @@ async def execute_ipython(
498497
# but the tool will fall back to config values via get_current_notebook_context()
499498

500499
return await safe_notebook_operation(
501-
lambda: ExecuteIpythonTool().execute(
500+
lambda: ExecuteCodeTool().execute(
502501
mode=server_context.mode,
503502
server_client=server_context.server_client,
504503
kernel_manager=server_context.kernel_manager,

jupyter_mcp_server/tools/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from jupyter_mcp_server.tools.execute_cell_tool import ExecuteCellTool
3232

3333
# Import tool implementations - Other Tools
34-
from jupyter_mcp_server.tools.execute_ipython_tool import ExecuteIpythonTool
34+
from jupyter_mcp_server.tools.execute_code_tool import ExecuteCodeTool
3535
from jupyter_mcp_server.tools.list_files_tool import ListFilesTool
3636
from jupyter_mcp_server.tools.list_kernels_tool import ListKernelsTool
3737

@@ -55,7 +55,7 @@
5555
# Cell Execution
5656
"ExecuteCellTool",
5757
# Other Tools
58-
"ExecuteIpythonTool",
58+
"ExecuteCodeTool",
5959
"ListFilesTool",
6060
"ListKernelsTool",
6161
]

jupyter_mcp_server/tools/execute_ipython_tool.py renamed to jupyter_mcp_server/tools/execute_code_tool.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
logger = logging.getLogger(__name__)
1717

1818

19-
class ExecuteIpythonTool(BaseTool):
20-
"""Execute IPython code directly in the kernel on the current active notebook"""
19+
class ExecuteCodeTool(BaseTool):
20+
"""Execute code directly in the kernel on the current active notebook"""
2121

2222
async def _execute_via_kernel_manager(
2323
self,
@@ -39,7 +39,7 @@ async def _execute_via_kernel_manager(
3939
# Use centralized execute_code_local function
4040
return await execute_code_local(
4141
serverapp=serverapp,
42-
notebook_path="", # Not needed for execute_ipython
42+
notebook_path="", # Not needed for execute_code
4343
code=code,
4444
kernel_id=kernel_id,
4545
timeout=timeout,
@@ -149,7 +149,7 @@ async def execute(
149149

150150
if kernel_id is None:
151151
# No kernel available - start a new one on demand
152-
logger.info("No kernel_id available, starting new kernel for execute_ipython")
152+
logger.info("No kernel_id available, starting new kernel for execute_code")
153153
kernel_id = await kernel_manager.start_kernel()
154154

155155
# Store the kernel in notebook_manager if available

tests/test_common.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"list_cells",
4141
"read_cell",
4242
"delete_cell",
43-
"execute_ipython",
43+
"execute_code",
4444
"list_files",
4545
"list_kernels"
4646
]
@@ -421,11 +421,11 @@ async def overwrite_cell_source(self, cell_index, cell_source):
421421
return self._get_structured_content_safe(result) if result else None
422422

423423
@requires_session
424-
async def execute_ipython(self, code, timeout=60):
425-
result = await self._session.call_tool("execute_ipython", arguments={"code": code, "timeout": timeout}) # type: ignore
424+
async def execute_code(self, code, timeout=60):
425+
result = await self._session.call_tool("execute_code", arguments={"code": code, "timeout": timeout}) # type: ignore
426426
structured = self._get_structured_content_safe(result)
427427

428-
# execute_ipython should always return a list of outputs
428+
# execute_code should always return a list of outputs
429429
# If we got a plain string, wrap it as a list
430430
if structured and "result" in structured:
431431
result_val = structured["result"]

tests/test_jupyter_extension.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
1111
Key differences from MCP_SERVER mode:
1212
- Uses YDoc collaborative editing when notebooks are open
13-
- Direct kernel_manager access for execute_ipython
1413
- Local file operations without HTTP roundtrip
1514
1615
The tests connect to the extension's HTTP endpoints (not the standalone MCP server).

tests/test_tools.py

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -559,22 +559,22 @@ async def test_notebooks_error_cases(mcp_client_parametrized: MCPClient):
559559

560560

561561
###############################################################################
562-
# execute_ipython Tests
562+
# execute_code Tests
563563
###############################################################################
564564

565565
@pytest.mark.asyncio
566566
@timeout_wrapper(30)
567-
async def test_execute_ipython_python_code(mcp_client_parametrized: MCPClient):
568-
"""Test execute_ipython with basic Python code in both modes"""
567+
async def test_execute_code_python_code(mcp_client_parametrized: MCPClient):
568+
"""Test execute_code with basic Python code in both modes"""
569569
async with mcp_client_parametrized:
570570
# Test simple Python code
571-
result = await mcp_client_parametrized.execute_ipython("print('Hello IPython World!')")
571+
result = await mcp_client_parametrized.execute_code("print('Hello IPython World!')")
572572

573573
# On Windows, if result is None it's likely due to timeout - skip the test
574574
if platform.system() == "Windows" and result is None:
575-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
575+
pytest.skip("execute_code timed out on Windows - known platform limitation")
576576

577-
assert result is not None, "execute_ipython result should not be None"
577+
assert result is not None, "execute_code result should not be None"
578578
assert "result" in result, "Result should contain 'result' key"
579579
outputs = result["result"]
580580
assert isinstance(outputs, list), "Outputs should be a list"
@@ -584,10 +584,10 @@ async def test_execute_ipython_python_code(mcp_client_parametrized: MCPClient):
584584
assert "Hello IPython World!" in output_text or "[No output generated]" in output_text
585585

586586
# Test mathematical calculation
587-
calc_result = await mcp_client_parametrized.execute_ipython("result = 2 ** 10\nprint(f'2^10 = {result}')")
587+
calc_result = await mcp_client_parametrized.execute_code("result = 2 ** 10\nprint(f'2^10 = {result}')")
588588

589589
if platform.system() == "Windows" and calc_result is None:
590-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
590+
pytest.skip("execute_code timed out on Windows - known platform limitation")
591591

592592
assert calc_result is not None
593593
calc_outputs = calc_result["result"]
@@ -597,38 +597,38 @@ async def test_execute_ipython_python_code(mcp_client_parametrized: MCPClient):
597597

598598
@pytest.mark.asyncio
599599
@timeout_wrapper(30)
600-
async def test_execute_ipython_magic_commands(mcp_client_parametrized: MCPClient):
601-
"""Test execute_ipython with IPython magic commands in both modes"""
600+
async def test_execute_code_magic_commands(mcp_client_parametrized: MCPClient):
601+
"""Test execute_code with IPython magic commands in both modes"""
602602
async with mcp_client_parametrized:
603603
# Test %who magic command (list variables)
604-
result = await mcp_client_parametrized.execute_ipython("%who")
604+
result = await mcp_client_parametrized.execute_code("%who")
605605

606606
# On Windows, if result is None it's likely due to timeout - skip the test
607607
if platform.system() == "Windows" and result is None:
608-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
608+
pytest.skip("execute_code timed out on Windows - known platform limitation")
609609

610-
assert result is not None, "execute_ipython result should not be None"
610+
assert result is not None, "execute_code result should not be None"
611611
outputs = result["result"]
612612
assert isinstance(outputs, list), "Outputs should be a list"
613613

614614
# Set a variable first, then use %who to see it
615-
var_result = await mcp_client_parametrized.execute_ipython("test_var = 42")
615+
var_result = await mcp_client_parametrized.execute_code("test_var = 42")
616616
if platform.system() == "Windows" and var_result is None:
617-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
617+
pytest.skip("execute_code timed out on Windows - known platform limitation")
618618

619-
who_result = await mcp_client_parametrized.execute_ipython("%who")
619+
who_result = await mcp_client_parametrized.execute_code("%who")
620620
if platform.system() == "Windows" and who_result is None:
621-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
621+
pytest.skip("execute_code timed out on Windows - known platform limitation")
622622

623623
who_outputs = who_result["result"]
624624
who_text = "".join(str(output) for output in who_outputs)
625625
# %who should show our variable (or no output if variables exist but aren't shown)
626626
# This test mainly ensures %who doesn't crash
627627

628628
# Test %timeit magic command
629-
timeit_result = await mcp_client_parametrized.execute_ipython("%timeit sum(range(100))")
629+
timeit_result = await mcp_client_parametrized.execute_code("%timeit sum(range(100))")
630630
if platform.system() == "Windows" and timeit_result is None:
631-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
631+
pytest.skip("execute_code timed out on Windows - known platform limitation")
632632

633633
assert timeit_result is not None
634634
timeit_outputs = timeit_result["result"]
@@ -639,17 +639,17 @@ async def test_execute_ipython_magic_commands(mcp_client_parametrized: MCPClient
639639

640640
@pytest.mark.asyncio
641641
@timeout_wrapper(30)
642-
async def test_execute_ipython_shell_commands(mcp_client_parametrized: MCPClient):
643-
"""Test execute_ipython with shell commands in both modes"""
642+
async def test_execute_code_shell_commands(mcp_client_parametrized: MCPClient):
643+
"""Test execute_code with shell commands in both modes"""
644644
async with mcp_client_parametrized:
645645
# Test basic shell command - echo (works on most systems)
646-
result = await mcp_client_parametrized.execute_ipython("!echo 'Hello from shell'")
646+
result = await mcp_client_parametrized.execute_code("!echo 'Hello from shell'")
647647

648648
# On Windows, if result is None it's likely due to timeout - skip the test
649649
if platform.system() == "Windows" and result is None:
650-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
650+
pytest.skip("execute_code timed out on Windows - known platform limitation")
651651

652-
assert result is not None, "execute_ipython result should not be None"
652+
assert result is not None, "execute_code result should not be None"
653653
outputs = result["result"]
654654
assert isinstance(outputs, list), "Outputs should be a list"
655655

@@ -658,9 +658,9 @@ async def test_execute_ipython_shell_commands(mcp_client_parametrized: MCPClient
658658
assert len(output_text) >= 0 # Just ensure no crash
659659

660660
# Test Python version check
661-
python_result = await mcp_client_parametrized.execute_ipython("!python --version")
661+
python_result = await mcp_client_parametrized.execute_code("!python --version")
662662
if platform.system() == "Windows" and python_result is None:
663-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
663+
pytest.skip("execute_code timed out on Windows - known platform limitation")
664664

665665
assert python_result is not None
666666
python_outputs = python_result["result"]
@@ -671,15 +671,15 @@ async def test_execute_ipython_shell_commands(mcp_client_parametrized: MCPClient
671671

672672
@pytest.mark.asyncio
673673
@timeout_wrapper(30)
674-
async def test_execute_ipython_timeout(mcp_client_parametrized: MCPClient):
675-
"""Test execute_ipython timeout functionality in both modes"""
674+
async def test_execute_code_timeout(mcp_client_parametrized: MCPClient):
675+
"""Test execute_code timeout functionality in both modes"""
676676
async with mcp_client_parametrized:
677677
# Test with very short timeout on a potentially long-running command
678-
result = await mcp_client_parametrized.execute_ipython("import time; time.sleep(5)", timeout=2)
678+
result = await mcp_client_parametrized.execute_code("import time; time.sleep(5)", timeout=2)
679679

680680
# On Windows, if result is None it's likely due to timeout - skip the test
681681
if platform.system() == "Windows" and result is None:
682-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
682+
pytest.skip("execute_code timed out on Windows - known platform limitation")
683683

684684
assert result is not None
685685
outputs = result["result"]
@@ -690,15 +690,15 @@ async def test_execute_ipython_timeout(mcp_client_parametrized: MCPClient):
690690

691691
@pytest.mark.asyncio
692692
@timeout_wrapper(30)
693-
async def test_execute_ipython_error_handling(mcp_client_parametrized: MCPClient):
694-
"""Test execute_ipython error handling in both modes"""
693+
async def test_execute_code_error_handling(mcp_client_parametrized: MCPClient):
694+
"""Test execute_code error handling in both modes"""
695695
async with mcp_client_parametrized:
696696
# Test syntax error
697-
result = await mcp_client_parametrized.execute_ipython("invalid python syntax <<<")
697+
result = await mcp_client_parametrized.execute_code("invalid python syntax <<<")
698698

699699
# On Windows, if result is None it's likely due to timeout - skip the test
700700
if platform.system() == "Windows" and result is None:
701-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
701+
pytest.skip("execute_code timed out on Windows - known platform limitation")
702702

703703
assert result is not None
704704
outputs = result["result"]
@@ -707,9 +707,9 @@ async def test_execute_ipython_error_handling(mcp_client_parametrized: MCPClient
707707
assert len(output_text) >= 0 # Ensure no crash
708708

709709
# Test runtime error
710-
runtime_result = await mcp_client_parametrized.execute_ipython("undefined_variable")
710+
runtime_result = await mcp_client_parametrized.execute_code("undefined_variable")
711711
if platform.system() == "Windows" and runtime_result is None:
712-
pytest.skip("execute_ipython timed out on Windows - known platform limitation")
712+
pytest.skip("execute_code timed out on Windows - known platform limitation")
713713

714714
assert runtime_result is not None
715715
runtime_outputs = runtime_result["result"]

0 commit comments

Comments
 (0)