Skip to main content
Glama
index.js14.4 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { JupyterHandler } from "./jupyter-handler.js"; import { VSCodeIntegration } from "./vscode-integration.js"; class JupyterCompleteServer { constructor() { this.server = new Server( { name: "mcp-jupyter-complete", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); this.jupyterHandler = new JupyterHandler(); this.vscodeIntegration = new VSCodeIntegration(); this.setupToolHandlers(); this.setupErrorHandling(); } setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // Position-based operations { name: "list_cells", description: "List all cells in a Jupyter notebook with their indices and types", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" } }, required: ["notebook_path"] } }, { name: "get_cell_source", description: "Get the source code of a specific cell by index", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" }, cell_index: { type: "integer", description: "Zero-based index of the cell" } }, required: ["notebook_path", "cell_index"] } }, { name: "edit_cell_source", description: "Edit the source code of a specific cell by index", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" }, cell_index: { type: "integer", description: "Zero-based index of the cell" }, new_source: { type: "string", description: "New source code for the cell" } }, required: ["notebook_path", "cell_index", "new_source"] } }, { name: "insert_cell", description: "Insert a new cell at a specific position", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" }, position: { type: "integer", description: "Position to insert the cell (0-based)" }, cell_type: { type: "string", enum: ["code", "markdown", "raw"], default: "code", description: "Type of cell to create" }, source: { type: "string", default: "", description: "Initial source code/content for the cell" } }, required: ["notebook_path", "position"] } }, { name: "delete_cell", description: "Delete a cell by index", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" }, cell_index: { type: "integer", description: "Zero-based index of the cell to delete" } }, required: ["notebook_path", "cell_index"] } }, // Enhanced operations { name: "move_cell", description: "Move a cell from one position to another", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" }, from_index: { type: "integer", description: "Current index of the cell" }, to_index: { type: "integer", description: "Target index for the cell" } }, required: ["notebook_path", "from_index", "to_index"] } }, { name: "convert_cell_type", description: "Convert a cell from one type to another", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" }, cell_index: { type: "integer", description: "Zero-based index of the cell" }, new_type: { type: "string", enum: ["code", "markdown", "raw"], description: "New cell type" } }, required: ["notebook_path", "cell_index", "new_type"] } }, { 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"] } }, // Kernel execution tools { name: "read_notebook_with_outputs", description: "Read a Jupyter notebook including cell outputs", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" } }, required: ["notebook_path"] } }, { name: "execute_cell", description: "Execute a specific cell in the notebook using a Jupyter kernel", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" }, cell_id: { type: ["string", "integer"], description: "Cell ID or zero-based index of the cell to execute" } }, required: ["notebook_path", "cell_id"] } }, { name: "add_cell", description: "Add a new cell to the notebook", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" }, source: { type: "string", default: "", description: "Initial source code/content for the cell" }, cell_type: { type: "string", enum: ["code", "markdown", "raw"], default: "code", description: "Type of cell to create" }, position: { type: "integer", description: "Position to insert the cell (defaults to end if not specified)" } }, required: ["notebook_path"] } }, { name: "edit_cell", description: "Edit the source code of a specific cell by ID or index", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" }, cell_id: { type: ["string", "integer"], description: "Cell ID or zero-based index of the cell to edit" }, new_source: { type: "string", description: "New source code for the cell" } }, required: ["notebook_path", "cell_id", "new_source"] } }, // VS Code integration { name: "trigger_vscode_reload", description: "Trigger VS Code to reload the notebook file", inputSchema: { type: "object", properties: { notebook_path: { type: "string", description: "Absolute path to the Jupyter notebook file" } }, required: ["notebook_path"] } } ] }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "list_cells": return await this.jupyterHandler.listCells(args.notebook_path); case "get_cell_source": return await this.jupyterHandler.getCellSource(args.notebook_path, args.cell_index); case "edit_cell_source": return await this.jupyterHandler.editCellSource( args.notebook_path, args.cell_index, args.new_source ); case "insert_cell": return await this.jupyterHandler.insertCell( args.notebook_path, args.position, args.cell_type || "code", args.source || "" ); case "delete_cell": return await this.jupyterHandler.deleteCell(args.notebook_path, args.cell_index); case "move_cell": return await this.jupyterHandler.moveCell( args.notebook_path, args.from_index, args.to_index ); case "convert_cell_type": return await this.jupyterHandler.convertCellType( args.notebook_path, args.cell_index, args.new_type ); case "bulk_edit_cells": return await this.jupyterHandler.bulkEditCells( args.notebook_path, args.operations ); case "read_notebook_with_outputs": return await this.jupyterHandler.readNotebookWithOutputs(args.notebook_path); case "execute_cell": return await this.jupyterHandler.executeCell(args.notebook_path, args.cell_id); case "add_cell": return await this.jupyterHandler.addCell( args.notebook_path, args.source, args.cell_type, args.position ); case "edit_cell": return await this.jupyterHandler.editCell( args.notebook_path, args.cell_id, args.new_source ); case "trigger_vscode_reload": return await this.vscodeIntegration.triggerReload(args.notebook_path); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: "text", text: `Error: ${error.message}` } ], isError: true }; } }); } setupErrorHandling() { this.server.onerror = (error) => { console.error("[MCP Error]", error); }; process.on('uncaughtException', (error) => { console.error("[Uncaught Exception]", error); }); process.on('unhandledRejection', (reason, promise) => { console.error("[Unhandled Rejection]", reason); }); process.on('SIGINT', async () => { await this.jupyterHandler.cleanup(); await this.server.close(); process.exit(0); }); process.on('SIGTERM', async () => { await this.jupyterHandler.cleanup(); await this.server.close(); process.exit(0); }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("MCP Jupyter Complete server running on stdio"); } } const server = new JupyterCompleteServer(); server.run().catch(console.error);

Implementation Reference

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/azharlabs/mcp-jupyter-server'

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