Skip to main content
Glama
notebook.py20.9 kB
""" Notebook Management Tools - Create, edit, and manage Jupyter notebooks """ import nbformat import json import subprocess from pathlib import Path from typing import Dict, Any, List, Optional from .kernel_setup import ensure_kernel_registered def get_kernel_connection_info() -> Optional[Dict[str, Any]]: """Get kernel connection info from the daemon's connection file""" # Look for the kernel connection file connection_file_paths = [ Path.cwd() / '.kernel_connection.json', Path(__file__).parent.parent / '.kernel_connection.json', # src/.kernel_connection.json Path(__file__).parent.parent.parent / '.kernel_connection.json', # project root Path.home() / '.kernel_connection.json' ] for conn_file in connection_file_paths: if conn_file.exists(): try: with open(conn_file, 'r') as f: return json.load(f) except Exception: continue return None def register(mcp): """Register notebook tools with the MCP server""" @mcp.tool() def jupyter_create_notebook( filename: str, session_id: Optional[str] = None, template: str = "default", title: Optional[str] = None ) -> Dict[str, Any]: """ Create a new Jupyter notebook with optional template. CLAUDE: Use to create structured notebooks for analysis or reports. Args: filename: Path for the notebook (must end with .ipynb) session_id: Optional session to attach kernel template: Template type ("default", "data_analysis", "ml_experiment", "visualization") title: Optional title for the notebook Returns: Notebook creation status """ notebook_path = Path(filename) # Ensure .ipynb extension if not notebook_path.suffix == '.ipynb': notebook_path = notebook_path.with_suffix('.ipynb') # Create notebook nb = nbformat.v4.new_notebook() # Add metadata with kernel connection if available kernel_info = get_kernel_connection_info() # Ensure kernel is registered ensure_kernel_registered() # Check if claude-jupy kernel is available try: result = subprocess.run(['jupyter', 'kernelspec', 'list'], capture_output=True, text=True) has_claude_kernel = 'claude-jupy' in result.stdout except: has_claude_kernel = False # Use claude-jupy kernel if available, otherwise python3 if has_claude_kernel: nb.metadata = { 'kernelspec': { 'display_name': 'Claude Jupy Environment', 'language': 'python', 'name': 'claude-jupy' }, 'language_info': { 'name': 'python', 'version': '3.11.0' } } else: nb.metadata = { 'kernelspec': { 'display_name': 'Python 3', 'language': 'python', 'name': 'python3' }, 'language_info': { 'name': 'python', 'version': '3.11.0' } } # If we have kernel connection info, add it to metadata if kernel_info and kernel_info.get('connection_file'): nb.metadata['kernel_info'] = { 'connection_file': kernel_info['connection_file'], 'session_id': session_id or 'default' } # Apply template if template == "data_analysis": cells = create_data_analysis_template(title or "Data Analysis") elif template == "ml_experiment": cells = create_ml_experiment_template(title or "Machine Learning Experiment") elif template == "visualization": cells = create_visualization_template(title or "Data Visualization") else: cells = create_default_template(title or "Jupyter Notebook") nb.cells = cells # Save notebook try: with open(notebook_path, 'w') as f: nbformat.write(nb, f) return { 'status': 'success', 'notebook_path': str(notebook_path), 'cells_created': len(cells), 'template_used': template, 'claude_next': f"Use jupyter_add_cell() to add more cells to {notebook_path}" } except Exception as e: return { 'status': 'error', 'error': str(e), 'claude_tip': "Check file permissions and path validity" } @mcp.tool() def jupyter_add_cell( notebook_path: str, cell_type: str, content: str, position: Optional[int] = None, execute: bool = False, session_id: Optional[str] = None ) -> Dict[str, Any]: """ Add a cell to an existing notebook. CLAUDE: Use to incrementally build notebooks. Args: notebook_path: Path to notebook cell_type: "code" or "markdown" content: Cell content position: Position to insert (None = append) execute: Whether to execute code cell immediately session_id: Session ID if executing Returns: Cell addition status """ notebook_path = Path(notebook_path) if not notebook_path.exists(): return { 'status': 'error', 'error': f"Notebook not found: {notebook_path}", 'claude_tip': "Create notebook first with jupyter_create_notebook()" } # Read notebook try: with open(notebook_path, 'r') as f: nb = nbformat.read(f, as_version=4) except Exception as e: return { 'status': 'error', 'error': f"Could not read notebook: {str(e)}" } # Update kernel info if available and not already set if 'kernel_info' not in nb.metadata: kernel_info = get_kernel_connection_info() if kernel_info and kernel_info.get('connection_file'): nb.metadata['kernel_info'] = { 'connection_file': kernel_info['connection_file'], 'session_id': session_id or 'default' } # Update kernelspec display name if 'kernelspec' in nb.metadata: nb.metadata['kernelspec']['display_name'] = 'Python 3 (MCP Kernel)' # Create new cell if cell_type == 'code': new_cell = nbformat.v4.new_code_cell(content) # Execute if requested if execute and session_id: from ..daemon import DaemonClient client = DaemonClient() result = client.execute_code(content) # Add outputs to cell outputs = [] for output in result.get('outputs', []): if output['type'] == 'stream': outputs.append(nbformat.v4.new_output( 'stream', name=output['name'], text=output['text'] )) elif output['type'] == 'execute_result': outputs.append(nbformat.v4.new_output( 'execute_result', data=output['data'], execution_count=output.get('execution_count', 1) )) elif output['type'] == 'error': outputs.append(nbformat.v4.new_output( 'error', ename=output['ename'], evalue=output['evalue'], traceback=output['traceback'] )) new_cell.outputs = outputs new_cell.execution_count = result.get('execution_count', 1) elif cell_type == 'markdown': new_cell = nbformat.v4.new_markdown_cell(content) else: return { 'status': 'error', 'error': f"Invalid cell type: {cell_type}", 'claude_tip': "Use 'code' or 'markdown'" } # Insert cell if position is None: nb.cells.append(new_cell) else: nb.cells.insert(position, new_cell) # Save notebook try: with open(notebook_path, 'w') as f: nbformat.write(nb, f) result = { 'status': 'success', 'cell_type': cell_type, 'position': position if position is not None else len(nb.cells) - 1, 'total_cells': len(nb.cells) } if execute: result['executed'] = True result['has_error'] = any(o.get('type') == 'error' for o in result.get('outputs', [])) return result except Exception as e: return { 'status': 'error', 'error': f"Could not save notebook: {str(e)}" } @mcp.tool() def jupyter_update_cell( notebook_path: str, cell_index: int, content: str, execute: bool = False, session_id: Optional[str] = None ) -> Dict[str, Any]: """ Update content of an existing cell. CLAUDE: Use to modify cells in a notebook. Args: notebook_path: Path to notebook cell_index: Index of cell to update (0-based) content: New cell content execute: Whether to execute if code cell session_id: Session ID if executing Returns: Update status """ notebook_path = Path(notebook_path) if not notebook_path.exists(): return { 'status': 'error', 'error': f"Notebook not found: {notebook_path}" } # Read notebook try: with open(notebook_path, 'r') as f: nb = nbformat.read(f, as_version=4) except Exception as e: return { 'status': 'error', 'error': f"Could not read notebook: {str(e)}" } # Update kernel info if available and not already set if 'kernel_info' not in nb.metadata: kernel_info = get_kernel_connection_info() if kernel_info and kernel_info.get('connection_file'): nb.metadata['kernel_info'] = { 'connection_file': kernel_info['connection_file'], 'session_id': session_id or 'default' } # Update kernelspec display name if 'kernelspec' in nb.metadata: nb.metadata['kernelspec']['display_name'] = 'Python 3 (MCP Kernel)' # Check cell index if cell_index >= len(nb.cells): return { 'status': 'error', 'error': f"Cell index {cell_index} out of range (notebook has {len(nb.cells)} cells)", 'claude_tip': "Use jupyter_get_notebook_info() to see cell count" } # Update cell cell = nb.cells[cell_index] old_content = cell.source cell.source = content # Execute if requested and it's a code cell if execute and cell.cell_type == 'code' and session_id: from ..daemon import DaemonClient client = DaemonClient() result = client.execute_code(content) # Update outputs outputs = [] for output in result.get('outputs', []): if output['type'] == 'stream': outputs.append(nbformat.v4.new_output( 'stream', name=output['name'], text=output['text'] )) elif output['type'] == 'execute_result': outputs.append(nbformat.v4.new_output( 'execute_result', data=output['data'], execution_count=output.get('execution_count', 1) )) elif output['type'] == 'error': outputs.append(nbformat.v4.new_output( 'error', ename=output['ename'], evalue=output['evalue'], traceback=output['traceback'] )) cell.outputs = outputs cell.execution_count = result.get('execution_count', 1) # Save notebook try: with open(notebook_path, 'w') as f: nbformat.write(nb, f) return { 'status': 'success', 'cell_index': cell_index, 'cell_type': cell.cell_type, 'old_content_preview': old_content[:100] if old_content else "", 'executed': execute and cell.cell_type == 'code' } except Exception as e: return { 'status': 'error', 'error': f"Could not save notebook: {str(e)}" } @mcp.tool() def jupyter_get_notebook_info(notebook_path: str) -> Dict[str, Any]: """ Get information about a notebook. CLAUDE: Use to understand notebook structure before editing. Args: notebook_path: Path to notebook Returns: Notebook metadata and cell information """ notebook_path = Path(notebook_path) if not notebook_path.exists(): return { 'status': 'error', 'error': f"Notebook not found: {notebook_path}" } # Read notebook try: with open(notebook_path, 'r') as f: nb = nbformat.read(f, as_version=4) except Exception as e: return { 'status': 'error', 'error': f"Could not read notebook: {str(e)}" } # Analyze cells cells_info = [] code_cells = 0 markdown_cells = 0 for i, cell in enumerate(nb.cells): cell_info = { 'index': i, 'type': cell.cell_type, 'content_preview': cell.source[:100] if cell.source else "", 'lines': len(cell.source.split('\n')) if cell.source else 0 } if cell.cell_type == 'code': code_cells += 1 cell_info['has_output'] = len(cell.outputs) > 0 if hasattr(cell, 'outputs') else False cell_info['execution_count'] = cell.execution_count if hasattr(cell, 'execution_count') else None else: markdown_cells += 1 cells_info.append(cell_info) return { 'notebook_path': str(notebook_path), 'total_cells': len(nb.cells), 'code_cells': code_cells, 'markdown_cells': markdown_cells, 'cells': cells_info, 'metadata': nb.metadata, 'claude_tip': f"Notebook has {len(nb.cells)} cells. Use cell indices 0-{len(nb.cells)-1} for operations." } @mcp.tool() def jupyter_save_notebook(session_id: str, notebook_path: str) -> Dict[str, Any]: """ Save the current session state to a notebook. CLAUDE: Use to save your work session as a notebook. Args: session_id: Current session ID notebook_path: Path to save notebook Returns: Save status """ # This would need implementation to track executed cells in session # For now, return a placeholder return { 'status': 'info', 'message': "Session saving not yet implemented", 'claude_tip': "Use jupyter_create_notebook() and jupyter_add_cell() to build notebooks" } def create_default_template(title: str) -> List: """Create default notebook template""" return [ nbformat.v4.new_markdown_cell(f"# {title}\n\nCreated with Jupyter MCP Server"), nbformat.v4.new_code_cell("# Import libraries\nimport pandas as pd\nimport numpy as np"), ] def create_data_analysis_template(title: str) -> List: """Create data analysis notebook template""" return [ nbformat.v4.new_markdown_cell(f"# {title}\n\n## Overview\nData analysis notebook created with Jupyter MCP Server"), nbformat.v4.new_markdown_cell("## 1. Setup and Imports"), nbformat.v4.new_code_cell("import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# Set style\nsns.set_style('whitegrid')\nplt.rcParams['figure.figsize'] = (10, 6)"), nbformat.v4.new_markdown_cell("## 2. Load Data"), nbformat.v4.new_code_cell("# Load your data here\n# df = pd.read_csv('data.csv')"), nbformat.v4.new_markdown_cell("## 3. Data Exploration"), nbformat.v4.new_code_cell("# Basic info\n# df.info()\n# df.describe()"), nbformat.v4.new_markdown_cell("## 4. Data Cleaning"), nbformat.v4.new_code_cell("# Handle missing values, outliers, etc."), nbformat.v4.new_markdown_cell("## 5. Analysis"), nbformat.v4.new_code_cell("# Perform your analysis"), nbformat.v4.new_markdown_cell("## 6. Visualization"), nbformat.v4.new_code_cell("# Create visualizations"), nbformat.v4.new_markdown_cell("## 7. Conclusions"), ] def create_ml_experiment_template(title: str) -> List: """Create ML experiment notebook template""" return [ nbformat.v4.new_markdown_cell(f"# {title}\n\n## Machine Learning Experiment"), nbformat.v4.new_markdown_cell("## 1. Imports and Setup"), nbformat.v4.new_code_cell("import pandas as pd\nimport numpy as np\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.metrics import classification_report, confusion_matrix\nimport matplotlib.pyplot as plt\nimport seaborn as sns"), nbformat.v4.new_markdown_cell("## 2. Load and Prepare Data"), nbformat.v4.new_code_cell("# Load data\n# df = pd.read_csv('data.csv')"), nbformat.v4.new_markdown_cell("## 3. Feature Engineering"), nbformat.v4.new_code_cell("# Create features"), nbformat.v4.new_markdown_cell("## 4. Train/Test Split"), nbformat.v4.new_code_cell("# X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)"), nbformat.v4.new_markdown_cell("## 5. Model Training"), nbformat.v4.new_code_cell("# Train your model"), nbformat.v4.new_markdown_cell("## 6. Evaluation"), nbformat.v4.new_code_cell("# Evaluate model performance"), nbformat.v4.new_markdown_cell("## 7. Results and Next Steps"), ] def create_visualization_template(title: str) -> List: """Create visualization notebook template""" return [ nbformat.v4.new_markdown_cell(f"# {title}\n\n## Data Visualization"), nbformat.v4.new_markdown_cell("## Setup"), nbformat.v4.new_code_cell("import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport plotly.express as px\n\n# Configure visualization settings\nsns.set_theme(style='whitegrid')\nplt.rcParams['figure.figsize'] = (12, 8)"), nbformat.v4.new_markdown_cell("## Load Data"), nbformat.v4.new_code_cell("# df = pd.read_csv('data.csv')"), nbformat.v4.new_markdown_cell("## Statistical Plots"), nbformat.v4.new_code_cell("# Distribution plots, correlations, etc."), nbformat.v4.new_markdown_cell("## Time Series Plots"), nbformat.v4.new_code_cell("# If applicable"), nbformat.v4.new_markdown_cell("## Interactive Visualizations"), nbformat.v4.new_code_cell("# Using plotly or similar"), ]

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mayank-ketkar-sf/ClaudeJupy'

If you have feedback or need assistance with the MCP directory API, please join our Discord server