bulk_edit_cells
Edit, delete, or convert multiple cells in Jupyter notebooks simultaneously to manage notebook content efficiently.
Instructions
Perform bulk operations on multiple cells
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| notebook_path | Yes | Absolute path to the Jupyter notebook file | |
| operations | Yes |
Input Schema (JSON Schema)
{
"properties": {
"notebook_path": {
"description": "Absolute path to the Jupyter notebook file",
"type": "string"
},
"operations": {
"items": {
"properties": {
"cell_index": {
"type": "integer"
},
"new_source": {
"type": "string"
},
"new_type": {
"enum": [
"code",
"markdown",
"raw"
],
"type": "string"
},
"type": {
"enum": [
"edit",
"delete",
"convert"
],
"type": "string"
}
},
"required": [
"type",
"cell_index"
],
"type": "object"
},
"type": "array"
}
},
"required": [
"notebook_path",
"operations"
],
"type": "object"
}
Implementation Reference
- src/jupyter-handler.js:313-399 (handler)The main handler function that performs bulk operations (edit, delete, convert) on multiple cells in a Jupyter notebook. It reads the notebook, processes operations in sorted order to handle deletions correctly, applies changes, writes back the notebook, and returns success/error summary.async bulkEditCells(notebookPath, operations) { const notebook = await this.readNotebook(notebookPath); let successCount = 0; const errors = []; // Sort operations by index in descending order for deletions const sortedOps = operations.sort((a, b) => { if (a.type === 'delete' && b.type !== 'delete') return -1; if (a.type !== 'delete' && b.type === 'delete') return 1; return b.cell_index - a.cell_index; }); for (const op of sortedOps) { try { switch (op.type) { case 'edit': if (op.cell_index < 0 || op.cell_index >= notebook.cells.length) { throw new Error(`Invalid cell index ${op.cell_index}`); } // Convert string to array format - each line should end with \n except the last const lines = op.new_source.split('\n'); const sourceArray = lines.map((line, index) => { if (index === lines.length - 1) { return line === '' ? '' : line; } else { return line + '\n'; } }); // Remove empty last element if original ended with \n if (sourceArray.length > 1 && sourceArray[sourceArray.length - 1] === '') { sourceArray.pop(); } notebook.cells[op.cell_index].source = sourceArray; break; case 'delete': if (op.cell_index < 0 || op.cell_index >= notebook.cells.length) { throw new Error(`Invalid cell index ${op.cell_index}`); } notebook.cells.splice(op.cell_index, 1); break; case 'convert': if (op.cell_index < 0 || op.cell_index >= notebook.cells.length) { throw new Error(`Invalid cell index ${op.cell_index}`); } this.validateCellType(op.new_type); const cell = notebook.cells[op.cell_index]; cell.cell_type = op.new_type; if (op.new_type === 'code') { cell.execution_count = null; cell.outputs = []; } else { delete cell.execution_count; delete cell.outputs; } break; default: throw new Error(`Unknown operation type: ${op.type}`); } successCount++; } catch (error) { errors.push(`Operation ${op.type} on cell ${op.cell_index}: ${error.message}`); } } await this.writeNotebook(notebookPath, notebook); const resultText = [ `Bulk operation completed: ${successCount}/${operations.length} operations successful` ]; if (errors.length > 0) { resultText.push(`\nErrors:\n${errors.join('\n')}`); } return { content: [ { type: "text", text: resultText.join('\n') } ] }; }
- src/index.js:187-222 (schema)Input schema definition for the bulk_edit_cells tool, specifying notebook_path and array of operations with types edit/delete/convert, including required fields and enums for cell types.name: "bulk_edit_cells", description: "Perform bulk operations on multiple cells", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" }, operations: { type: "array", items: { type: "object", properties: { type: { type: "string", enum: ["edit", "delete", "convert"] }, cell_index: { type: "integer" }, new_source: { type: "string" }, new_type: { type: "string", enum: ["code", "markdown", "raw"] } }, required: ["type", "cell_index"] } } }, required: ["notebook_path", "operations"] } },
- src/index.js:369-373 (registration)Tool registration in the MCP server request handler switch statement, delegating to jupyterHandler.bulkEditCells method.case "bulk_edit_cells": return await this.jupyterHandler.bulkEditCells( args.notebook_path, args.operations );