Skip to content

Commit 35ec316

Browse files
authored
Merge pull request #1 from Zsailer/working-server
Initial implementation of an MCP server as an extension
2 parents fb0b9a5 + cdcb111 commit 35ec316

File tree

12 files changed

+1308
-2
lines changed

12 files changed

+1308
-2
lines changed

.github/workflows/tests.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [ main, dev ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ${{ matrix.os }}
12+
strategy:
13+
matrix:
14+
os: [ubuntu-latest, macos-latest, windows-latest]
15+
python-version: ['3.10', '3.11', '3.12']
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set up Python ${{ matrix.python-version }}
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
25+
- name: Install dependencies
26+
run: |
27+
python -m pip install --upgrade pip
28+
pip install -e .[test]
29+
30+
- name: Run linting
31+
run: |
32+
# Install linting tools
33+
pip install flake8 black isort
34+
# Check formatting
35+
black --check --diff .
36+
# Check import sorting
37+
isort --check-only --diff .
38+
# Run flake8
39+
flake8 jupyter_server_docs_mcp tests
40+
41+
- name: Run unit tests
42+
run: |
43+
pytest tests/ -v -m "not slow and not integration" --cov=jupyter_server_docs_mcp --cov-report=xml
44+
45+
- name: Run integration tests
46+
run: |
47+
pytest tests/ -v -m "integration"
48+
49+
- name: Upload coverage reports to Codecov
50+
uses: codecov/codecov-action@v4
51+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
52+
with:
53+
token: ${{ secrets.CODECOV_TOKEN }}
54+
file: ./coverage.xml
55+
fail_ci_if_error: true
56+
57+
test-optional-deps:
58+
runs-on: ubuntu-latest
59+
strategy:
60+
matrix:
61+
extra: [fastmcp, jupyterlab, full]
62+
63+
steps:
64+
- uses: actions/checkout@v4
65+
66+
- name: Set up Python 3.11
67+
uses: actions/setup-python@v5
68+
with:
69+
python-version: '3.11'
70+
71+
- name: Install with ${{ matrix.extra }} dependencies
72+
run: |
73+
python -m pip install --upgrade pip
74+
pip install -e .[${{ matrix.extra }},test]
75+
76+
- name: Run tests with optional dependencies
77+
run: |
78+
pytest tests/ -v --tb=short

README.md

Lines changed: 217 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,217 @@
1-
# jupyter-server-docs-mcp
2-
An MCP server for Jupyter Server Documents
1+
# Jupyter Server MCP Extension
2+
3+
A configurable MCP (Model Context Protocol) server extension for Jupyter Server that allows dynamic registration of Python functions as tools accessible to MCP clients from a running Jupyter Server.
4+
5+
## Overview
6+
7+
This extension provides a simplified, trait-based approach to exposing Jupyter functionality through the MCP protocol. It can dynamically load and register tools from various Python packages, making them available to AI assistants and other MCP clients.
8+
9+
## Key Features
10+
11+
- **Simplified Architecture**: Direct function registration without complex abstractions
12+
- **Configurable Tool Loading**: Register tools via string specifications (`module:function`)
13+
- **Jupyter Integration**: Seamless integration with Jupyter Server extension system
14+
- **HTTP Transport**: FastMCP-based HTTP server with proper MCP protocol support
15+
- **Traitlets Configuration**: Full configuration support through Jupyter's traitlets system
16+
17+
## Installation
18+
19+
```bash
20+
pip install -e .
21+
```
22+
23+
## Quick Start
24+
25+
### 1. Basic Configuration
26+
27+
Create a `jupyter_config.py` file:
28+
29+
```python
30+
c = get_config()
31+
32+
# Basic MCP server settings
33+
c.MCPExtensionApp.mcp_name = "My Jupyter MCP Server"
34+
c.MCPExtensionApp.mcp_port = 8080
35+
36+
# Register tools from existing packages
37+
c.MCPExtensionApp.mcp_tools = [
38+
# Standard library tools
39+
"os:getcwd",
40+
"json:dumps",
41+
"time:time",
42+
43+
# Jupyter AI Tools - Notebook operations
44+
"jupyter_ai_tools.toolkits.notebook:read_notebook",
45+
"jupyter_ai_tools.toolkits.notebook:edit_cell",
46+
47+
# JupyterLab Commands Toolkit
48+
"jupyterlab_commands_toolkit.tools:clear_all_outputs_in_notebook",
49+
"jupyterlab_commands_toolkit.tools:open_document",
50+
]
51+
```
52+
53+
### 2. Start Jupyter Server
54+
55+
```bash
56+
jupyter lab --config=jupyter_config.py
57+
```
58+
59+
The MCP server will start automatically on `http://localhost:8080/mcp`.
60+
61+
### 3. Connect MCP Clients
62+
63+
**Claude Code Configuration:**
64+
```json
65+
{
66+
"mcpServers": {
67+
"jupyter-mcp": {
68+
"command": "python",
69+
"args": ["-c", "pass"],
70+
"transport": {
71+
"type": "http",
72+
"url": "http://localhost:8080/mcp"
73+
}
74+
}
75+
}
76+
}
77+
```
78+
79+
## Architecture
80+
81+
### Core Components
82+
83+
#### MCPServer (`jupyter_server_docs_mcp.mcp_server.MCPServer`)
84+
85+
A simplified LoggingConfigurable class that manages FastMCP integration:
86+
87+
```python
88+
from jupyter_server_docs_mcp.mcp_server import MCPServer
89+
90+
# Create server
91+
server = MCPServer(name="My Server", port=8080)
92+
93+
# Register functions
94+
def my_tool(message: str) -> str:
95+
return f"Hello, {message}!"
96+
97+
server.register_tool(my_tool)
98+
99+
# Start server
100+
await server.start_server()
101+
```
102+
103+
**Key Methods:**
104+
- `register_tool(func, name=None, description=None)` - Register a Python function
105+
- `register_tools(tools)` - Register multiple functions (list or dict)
106+
- `list_tools()` - Get list of registered tools
107+
- `start_server(host=None)` - Start the HTTP MCP server
108+
109+
#### MCPExtensionApp (`jupyter_server_docs_mcp.extension.MCPExtensionApp`)
110+
111+
Jupyter Server extension that manages the MCP server lifecycle:
112+
113+
**Configuration Traits:**
114+
- `mcp_name` - Server name (default: "Jupyter MCP Server")
115+
- `mcp_port` - Server port (default: 3001)
116+
- `mcp_tools` - List of tools to register (format: "module:function")
117+
118+
### Tool Loading System
119+
120+
Tools are loaded using string specifications in the format `module_path:function_name`:
121+
122+
```python
123+
# Examples
124+
"os:getcwd" # Standard library
125+
"jupyter_ai_tools.toolkits.notebook:read_notebook" # External package
126+
"math:sqrt" # Built-in modules
127+
```
128+
129+
The extension dynamically imports the module and registers the function with FastMCP.
130+
131+
## Configuration Examples
132+
133+
### Minimal Setup
134+
```python
135+
c = get_config()
136+
c.MCPExtensionApp.mcp_port = 8080
137+
```
138+
139+
### Full Configuration
140+
```python
141+
c = get_config()
142+
143+
# MCP Server Configuration
144+
c.MCPExtensionApp.mcp_name = "Advanced Jupyter MCP Server"
145+
c.MCPExtensionApp.mcp_port = 8080
146+
c.MCPExtensionApp.mcp_tools = [
147+
# File system operations (jupyter-ai-tools)
148+
"jupyter_ai_tools.toolkits.file_system:read",
149+
"jupyter_ai_tools.toolkits.file_system:write",
150+
"jupyter_ai_tools.toolkits.file_system:edit",
151+
"jupyter_ai_tools.toolkits.file_system:ls",
152+
"jupyter_ai_tools.toolkits.file_system:glob",
153+
154+
# Notebook operations (jupyter-ai-tools)
155+
"jupyter_ai_tools.toolkits.notebook:read_notebook",
156+
"jupyter_ai_tools.toolkits.notebook:edit_cell",
157+
"jupyter_ai_tools.toolkits.notebook:add_cell",
158+
"jupyter_ai_tools.toolkits.notebook:delete_cell",
159+
"jupyter_ai_tools.toolkits.notebook:create_notebook",
160+
161+
# Git operations (jupyter-ai-tools)
162+
"jupyter_ai_tools.toolkits.git:git_status",
163+
"jupyter_ai_tools.toolkits.git:git_add",
164+
"jupyter_ai_tools.toolkits.git:git_commit",
165+
"jupyter_ai_tools.toolkits.git:git_push",
166+
167+
# JupyterLab operations (jupyterlab-commands-toolkit)
168+
"jupyterlab_commands_toolkit.tools:clear_all_outputs_in_notebook",
169+
"jupyterlab_commands_toolkit.tools:open_document",
170+
"jupyterlab_commands_toolkit.tools:open_markdown_file_in_preview_mode",
171+
"jupyterlab_commands_toolkit.tools:show_diff_of_current_notebook",
172+
173+
# Utility functions
174+
"os:getcwd",
175+
"json:dumps",
176+
"time:time",
177+
"platform:system",
178+
]
179+
```
180+
181+
### Running Tests
182+
183+
```bash
184+
# Install development dependencies
185+
pip install -e ".[dev]"
186+
187+
# Run tests
188+
pytest tests/ -v
189+
190+
# Run with coverage
191+
pytest --cov=jupyter_server_docs_mcp tests/
192+
```
193+
194+
### Project Structure
195+
196+
```
197+
jupyter_server_docs_mcp/
198+
├── jupyter_server_docs_mcp/
199+
│ ├── __init__.py
200+
│ ├── mcp_server.py # Core MCP server implementation
201+
│ └── extension.py # Jupyter Server extension
202+
├── tests/
203+
│ ├── test_mcp_server.py # MCPServer tests
204+
│ └── test_extension.py # Extension tests
205+
├── demo/
206+
│ ├── jupyter_config.py # Example configuration
207+
│ └── *.py # Debug/diagnostic scripts
208+
└── pyproject.toml # Package configuration
209+
```
210+
211+
## Contributing
212+
213+
1. Fork the repository
214+
2. Create a feature branch
215+
3. Add tests for new functionality
216+
4. Ensure all tests pass: `pytest tests/`
217+
5. Submit a pull request
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"ServerApp": {
3+
"jpserver_extensions": {
4+
"jupyter_server_docs_mcp": true
5+
}
6+
}
7+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Jupyter Server MCP Extension with configurable tools."""
2+
3+
__version__ = "0.1.0"
4+
5+
from .extension import MCPExtensionApp
6+
from typing import Any, Dict, List
7+
8+
9+
def _jupyter_server_extension_points() -> List[Dict[str, Any]]: # pragma: no cover
10+
return [
11+
{
12+
"module": "jupyter_server_docs_mcp.extension",
13+
"app": MCPExtensionApp,
14+
},
15+
]
16+
17+
__all__ = ["MCPExtensionApp"]

0 commit comments

Comments
 (0)