11"""Jupyter Server extension for managing MCP server."""
22
33import asyncio
4+ import contextlib
45import importlib
56import logging
6- from typing import Optional
77
88from jupyter_server .extension .application import ExtensionApp
99from traitlets import Int , List , Unicode
@@ -17,14 +17,12 @@ class MCPExtensionApp(ExtensionApp):
1717 """The Jupyter Server MCP extension app."""
1818
1919 name = "jupyter_server_mcp"
20- description = (
21- "Jupyter Server extension providing MCP server for tool registration"
22- )
20+ description = "Jupyter Server extension providing MCP server for tool registration"
2321
2422 # Configurable traits
25- mcp_port = Int (
26- default_value = 3001 , help = "Port for the MCP server to listen on"
27- ). tag ( config = True )
23+ mcp_port = Int (default_value = 3001 , help = "Port for the MCP server to listen on" ). tag (
24+ config = True
25+ )
2826
2927 mcp_name = Unicode (
3028 default_value = "Jupyter MCP Server" , help = "Name for the MCP server"
@@ -40,8 +38,8 @@ class MCPExtensionApp(ExtensionApp):
4038 ),
4139 ).tag (config = True )
4240
43- mcp_server_instance : Optional [ object ] = None
44- mcp_server_task : Optional [ asyncio .Task ] = None
41+ mcp_server_instance : object | None = None
42+ mcp_server_task : asyncio .Task | None = None
4543
4644 def _load_function_from_string (self , tool_spec : str ):
4745 """Load a function from a string specification.
@@ -59,25 +57,23 @@ def _load_function_from_string(self, tool_spec: str):
5957 AttributeError: If function not found in module
6058 """
6159 if ":" not in tool_spec :
62- raise ValueError (
60+ msg = (
6361 f"Invalid tool specification '{ tool_spec } '. "
6462 f"Expected format: 'module_path:function_name'"
6563 )
64+ raise ValueError (msg )
6665
6766 module_path , function_name = tool_spec .rsplit (":" , 1 )
6867
6968 try :
7069 module = importlib .import_module (module_path )
71- function = getattr (module , function_name )
72- return function
70+ return getattr (module , function_name )
7371 except ImportError as e :
74- raise ImportError (
75- f"Could not import module '{ module_path } ': { e } "
76- )
72+ msg = f"Could not import module '{ module_path } ': { e } "
73+ raise ImportError (msg ) from e
7774 except AttributeError as e :
78- raise AttributeError (
79- f"Function '{ function_name } ' not found in module '{ module_path } ': { e } "
80- )
75+ msg = f"Function '{ function_name } ' not found in module '{ module_path } ': { e } "
76+ raise AttributeError (msg ) from e
8177
8278 def _register_configured_tools (self ):
8379 """Register tools specified in the mcp_tools configuration."""
@@ -92,9 +88,7 @@ def _register_configured_tools(self):
9288 self .mcp_server_instance .register_tool (function )
9389 logger .info (f"✅ Registered tool: { tool_spec } " )
9490 except Exception as e :
95- logger .error (
96- f"❌ Failed to register tool '{ tool_spec } ': { e } "
97- )
91+ logger .error (f"❌ Failed to register tool '{ tool_spec } ': { e } " )
9892 continue
9993
10094 def initialize (self ):
@@ -105,12 +99,10 @@ def initialize(self):
10599 def initialize_handlers (self ):
106100 """Initialize the handlers for the extension."""
107101 # No HTTP handlers needed - MCP server runs on separate port
108- pass
109102
110103 def initialize_settings (self ):
111104 """Initialize settings for the extension."""
112105 # Configuration is handled by traitlets
113- pass
114106
115107 async def start_extension (self ):
116108 """Start the extension - called after Jupyter Server starts."""
@@ -137,19 +129,15 @@ async def start_extension(self):
137129 self .log .info (f"✅ MCP server started on port { self .mcp_port } " )
138130 if self .mcp_tools :
139131 registered_count = len (self .mcp_server_instance ._registered_tools )
140- self .log .info (
141- f"Registered { registered_count } tools from configuration"
142- )
132+ self .log .info (f"Registered { registered_count } tools from configuration" )
143133 else :
144- self .log .info (
145- "Use mcp_server_instance.register_tool() to add tools"
146- )
134+ self .log .info ("Use mcp_server_instance.register_tool() to add tools" )
147135
148136 except Exception as e :
149137 self .log .error (f"Failed to start MCP server: { e } " )
150138 raise
151139
152- async def _start_jupyter_server_extension (self , serverapp ):
140+ async def _start_jupyter_server_extension (self , serverapp ): # noqa: ARG002
153141 """Start the extension - called after Jupyter Server starts."""
154142 await self .start_extension ()
155143
@@ -158,10 +146,8 @@ async def stop_extension(self):
158146 if self .mcp_server_task and not self .mcp_server_task .done ():
159147 self .log .info ("Stopping MCP server" )
160148 self .mcp_server_task .cancel ()
161- try :
149+ with contextlib . suppress ( asyncio . CancelledError ) :
162150 await self .mcp_server_task
163- except asyncio .CancelledError :
164- pass
165151
166152 # Always clean up
167153 self .mcp_server_task = None
0 commit comments