@notebook-intelligence/notebook-intelligence 2.4.2 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -94,18 +94,18 @@ jupyter lab --NotebookIntelligence.notebook_execute_tool=env_enabled
94
94
 
95
95
  ### Configuration files
96
96
 
97
- NBI saves configuration at `~/.jupyter/nbi-config.json`. It also supports environment wide base configuration at `<env-prefix>/share/jupyter/nbi-config.json`. Organizations can ship default configuration at this environment wide config path. User's changes will be stored as overrides at `~/.jupyter/nbi-config.json`.
97
+ NBI saves configuration at `~/.jupyter/nbi/config.json`. It also supports environment wide base configuration at `<env-prefix>/share/jupyter/nbi/config.json`. Organizations can ship default configuration at this environment wide config path. User's changes will be stored as overrides at `~/.jupyter/nbi/config.json`.
98
98
 
99
99
  These config files are used for saving LLM provider, model and MCP configuration. Note that API keys you enter for your custom LLM providers will also be stored in these config files.
100
100
 
101
101
  > [!IMPORTANT]
102
- > Note that updating nbi-config.json manually requires restarting JupyterLab to take effect.
102
+ > Note that updating config.json manually requires restarting JupyterLab to take effect.
103
103
 
104
104
  ### Model Context Protocol ([MCP](https://modelcontextprotocol.io)) Support
105
105
 
106
106
  NBI seamlessly integrates with MCP servers. It supports servers with both Standard Input/Output (stdio) and Server-Sent Events (SSE) transports. The MCP support is limited to server tools at the moment.
107
107
 
108
- You can easily add MCP servers to NBI by editing the configuration file [nbi-config.json](#configuration-files). Simply add a key "mcp" and "mcpServers" under it as shown below.
108
+ You can easily add MCP servers to NBI by editing the configuration file [~/.jupyter/nbi/mcp.json](#configuration-files). Environment wide base configuration is also support using the file at `<env-prefix>/share/jupyter/nbi/mcp.json`.
109
109
 
110
110
  > [!NOTE]
111
111
  > Using MCP servers requires an LLM model with tool calling capabilities. All of the GitHub Copilot models provided in NBI support this feature. If you are using other providers make sure you choose a tool calling capable model.
@@ -113,47 +113,24 @@ You can easily add MCP servers to NBI by editing the configuration file [nbi-con
113
113
  > [!CAUTION]
114
114
  > Note that most MCP servers are run on the same computer as your JupyterLab installation and they can make irreversible changes to your computer and/or access private data. Make sure that you only install MCP servers from trusted sources.
115
115
 
116
+ ### MCP Config file example
117
+
116
118
  ```json
117
119
  {
118
- "chat_model": {
119
- ...
120
- },
121
- ...<other configuration>,
122
-
123
- "mcp": {
124
- "mcpServers": {
125
- "filesystem": {
126
- "command": "npx",
127
- "args": [
128
- "-y",
129
- "@modelcontextprotocol/server-filesystem",
130
- "/Users/mbektas/mcp-test"
131
- ]
132
- },
133
- }
120
+ "mcpServers": {
121
+ "filesystem": {
122
+ "command": "npx",
123
+ "args": [
124
+ "-y",
125
+ "@modelcontextprotocol/server-filesystem",
126
+ "/Users/mbektas/mcp-test"
127
+ ]
134
128
  }
129
+ }
135
130
  }
136
131
  ```
137
132
 
138
- This will automatically create a new chat participant in NBI and you can access it by starting your prompts with `@mcp`. Use `@mcp /info` prompt to get information on the tools provided by the MCP servers you configured. This chat participant will have access all the tools provided by the servers you configure.
139
-
140
- <img src="media/mcp-prompt.png" alt="Settings dialog" width=600 />
141
-
142
- By default, each tool call to MCP servers will require approval. If you would like to auto approve tools, you can do so by using the `"alwaysAllow": []` configuration key in the nbi-config.json. Simply list the names of tools.
143
-
144
- ```json
145
- "mcpServers": {
146
- "filesystem": {
147
- "command": "npx",
148
- "args": [
149
- "-y",
150
- "@modelcontextprotocol/server-filesystem",
151
- "/Users/mbektas/mcp-test"
152
- ],
153
- "alwaysAllow": ["list_allowed_directories", "list_directory"]
154
- },
155
- }
156
- ```
133
+ You can use Agent mode to access tools provided by MCP servers you configured.
157
134
 
158
135
  For servers with stdio transport, you can also set additional environment variables by using the `env` key. Environment variables are specified as key value pairs.
159
136
 
@@ -169,12 +146,12 @@ For servers with stdio transport, you can also set additional environment variab
169
146
  }
170
147
  ```
171
148
 
172
- Below is an example of a server configuration with SSE transport. For SSE transport servers, you can also specify headers to be sent as part of the requests.
149
+ Below is an example of a server configuration with Streamable HTTP transport. For Streamable HTTP transport servers, you can also specify headers to be sent as part of the requests.
173
150
 
174
151
  ```json
175
152
  "mcpServers": {
176
153
  "remoterservername": {
177
- "url": "http://127.0.0.1:8080/sse",
154
+ "url": "http://127.0.0.1:8080/mcp",
178
155
  "headers": {
179
156
  "Authorization": "Bearer mysecrettoken"
180
157
  }
@@ -198,77 +175,119 @@ If you have multiple servers configured but you would like to disable some for a
198
175
  }
199
176
  ```
200
177
 
201
- #### Grouping MCP servers
178
+ ### Ruleset System
202
179
 
203
- When you integrate multiple MCP servers to NBI, all of their tools will be available under the same chat participant `@mcp`. However, this may not be ideal in many situations. You may want to group certain servers and their tools based on their functionality. NBI lets you do that easily by configuring MCP chat participants. You can list the servers for each custom participant. If there are any unassigned MCP servers, then they will be used the default `@mcp` chat participant.
180
+ NBI includes a powerful ruleset system that allows you to define custom guidelines and best practices that are automatically injected into AI prompts. This helps ensure consistent coding standards, project-specific conventions, and domain knowledge across all AI interactions.
204
181
 
205
- Below is an example of creating a custom MCP participant. This configuration results in two chat participants `@mcp-fs` with `filesytem` MC server tools and `@mcp` with `servername1` and `servername1` MCP server tools.
182
+ #### How It Works
206
183
 
207
- ```json
208
- {
209
- "chat_model": {
210
- ...
211
- },
212
- ...<other configuration>,
213
-
214
- "mcp": {
215
- "mcpServers": {
216
- "filesystem": {
217
- "command": "npx",
218
- "args": [
219
- "-y",
220
- "@modelcontextprotocol/server-filesystem",
221
- "/Users/mbektas/mcp-test"
222
- ]
223
- },
224
- "servername1": {
225
- "command": "",
226
- "args": [],
227
- },
228
- "servername2": {
229
- "command": "",
230
- "args": [],
231
- "disabled": true
232
- }
233
- },
234
- "participants": {
235
- "fs": {
236
- "name": "MCP - File system",
237
- "servers": ["filesystem"]
238
- }
239
- }
240
- }
241
- }
184
+ Rules are markdown files with optional YAML frontmatter stored in `~/.jupyter/nbi/rules/`. They are automatically discovered and applied based on context (file type, notebook kernel, chat mode).
185
+
186
+ #### Creating Rules
187
+
188
+ **Global Rules** - Apply to all contexts:
189
+
190
+ Create a file like `~/.jupyter/nbi/rules/01-coding-standards.md`:
191
+
192
+ ```markdown
193
+ ---
194
+ priority: 10
195
+ ---
196
+
197
+ # Coding Standards
198
+
199
+ - Always use type hints in Python functions
200
+ - Prefer list comprehensions over loops when appropriate
201
+ - Add docstrings to all public functions
242
202
  ```
243
203
 
244
- #### Using NBI tools within MCP chat participants
204
+ **Mode-Specific Rules** - Apply only to specific chat modes:
245
205
 
246
- NBI allows you to access built-in tools from an MCP participant. You can do that by adding the list of built in NBI tools to your MCP participant configuration. The built-in tools available to MCP are `create_new_notebook`, `add_markdown_cell_to_notebook`, `add_code_cell_to_notebook`. Below is an example that integrates all these tools to MCP participant `@mcp-fs`.
206
+ NBI supports mode-specific rules for three modes:
207
+
208
+ - **ask** - Question/answer mode
209
+ - **agent** - Autonomous agent mode with tool access
210
+ - **inline-chat** - Inline code generation and editing
211
+
212
+ Create a file like `~/.jupyter/nbi/rules/modes/agent/01-testing.md`:
213
+
214
+ ```markdown
215
+ ---
216
+ priority: 20
217
+ scope:
218
+ kernels: ['python3']
219
+ ---
220
+
221
+ # Testing Guidelines
222
+
223
+ When writing code in agent mode:
224
+
225
+ - Always include error handling
226
+ - Add logging for debugging
227
+ - Test edge cases
228
+ ```
229
+
230
+ #### Rule Frontmatter Options
231
+
232
+ ```yaml
233
+ ---
234
+ apply: always # 'always', 'auto', or 'manual'
235
+ active: true # Enable/disable the rule
236
+ priority: 10 # Lower numbers = higher priority
237
+ scope:
238
+ file_patterns: # Apply to specific file patterns
239
+ - '*.py'
240
+ - 'test_*.ipynb'
241
+ kernels: # Apply to specific notebook kernels
242
+ - 'python3'
243
+ - 'ir'
244
+ directories: # Apply to specific directories
245
+ - '/projects/ml'
246
+ ---
247
+ ```
248
+
249
+ #### Configuration
250
+
251
+ **Enable/Disable Rules System:**
252
+
253
+ Edit `~/.jupyter/nbi/config.json`:
247
254
 
248
255
  ```json
249
- "participants": {
250
- "fs": {
251
- "name": "MCP - File system",
252
- "servers": ["filesystem"],
253
- "nbiTools": [
254
- "create_new_notebook",
255
- "add_markdown_cell_to_notebook",
256
- "add_code_cell_to_notebook"
257
- ]
258
- }
256
+ {
257
+ "rules_enabled": true
259
258
  }
260
259
  ```
261
260
 
262
- This chat participant will allow you to run example prompts like below.
261
+ **Auto-Reload Configuration:**
263
262
 
264
- ```
265
- @mcp-fs list the directories I have access to.
266
- ```
263
+ Rules are automatically reloaded when changed (enabled by default). This behavior is controlled by the `NBI_RULES_AUTO_RELOAD` environment variable.
267
264
 
265
+ To disable auto-reload:
266
+
267
+ ```bash
268
+ export NBI_RULES_AUTO_RELOAD=false
269
+ jupyter lab
268
270
  ```
269
- @mcp-fs add a code cell which demonstrates ipywidgets Button to this notebook.
271
+
272
+ Or to enable (default):
273
+
274
+ ```bash
275
+ export NBI_RULES_AUTO_RELOAD=true
276
+ jupyter lab
270
277
  ```
271
278
 
279
+ #### Managing Rules
280
+
281
+ Rules are automatically discovered from:
282
+
283
+ - **Global rules**: `~/.jupyter/nbi/rules/*.md`
284
+ - **Mode-specific rules**: `~/.jupyter/nbi/rules/modes/{mode}/*.md` where `{mode}` can be:
285
+ - `ask` - For question/answer interactions
286
+ - `agent` - For autonomous agent operations
287
+ - `inline-chat` - For inline code generation
288
+
289
+ Rules are applied in priority order (lower numbers first) and can be toggled on/off without deleting the files.
290
+
272
291
  ### Developer documentation
273
292
 
274
293
  For building locally and contributing see the [developer documentatation](CONTRIBUTING.md).
package/lib/api.d.ts CHANGED
@@ -22,6 +22,10 @@ export declare class NBIConfig {
22
22
  get usingGitHubCopilotModel(): boolean;
23
23
  get storeGitHubAccessToken(): boolean;
24
24
  get toolConfig(): any;
25
+ get mcpServers(): any;
26
+ getMCPServer(serverId: string): any;
27
+ getMCPServerPrompt(serverId: string, promptName: string): any;
28
+ get mcpServerSettings(): any;
25
29
  capabilities: any;
26
30
  chatParticipants: IChatParticipant[];
27
31
  changed: Signal<this, void>;
@@ -38,16 +42,19 @@ export declare class NBIAPI {
38
42
  static initializeWebsocket(): Promise<void>;
39
43
  static getLoginStatus(): GitHubCopilotLoginStatus;
40
44
  static getDeviceVerificationInfo(): IDeviceVerificationInfo;
45
+ static getGHLoginRequired(): boolean;
46
+ static getChatEnabled(): any;
47
+ static getInlineCompletionEnabled(): any;
41
48
  static loginToGitHub(): Promise<unknown>;
42
49
  static logoutFromGitHub(): Promise<unknown>;
43
50
  static updateGitHubLoginStatus(): Promise<void>;
44
51
  static fetchCapabilities(): Promise<void>;
45
52
  static setConfig(config: any): Promise<void>;
46
53
  static updateOllamaModelList(): Promise<void>;
47
- static reloadMCPServerList(): Promise<any>;
48
54
  static getMCPConfigFile(): Promise<any>;
49
55
  static setMCPConfigFile(config: any): Promise<any>;
50
56
  static chatRequest(messageId: string, chatId: string, prompt: string, language: string, filename: string, additionalContext: IContextItem[], chatMode: string, toolSelections: IToolSelections, responseEmitter: IChatCompletionResponseEmitter): Promise<void>;
57
+ static reloadMCPServers(): Promise<any>;
51
58
  static generateCode(chatId: string, prompt: string, prefix: string, suffix: string, existingCode: string, language: string, filename: string, responseEmitter: IChatCompletionResponseEmitter): Promise<void>;
52
59
  static sendChatUserInput(messageId: string, data: any): Promise<void>;
53
60
  static sendWebSocketMessage(messageId: string, messageType: RequestDataType, data: any): Promise<void>;
package/lib/api.js CHANGED
@@ -53,6 +53,22 @@ export class NBIConfig {
53
53
  get toolConfig() {
54
54
  return this.capabilities.tool_config;
55
55
  }
56
+ get mcpServers() {
57
+ return this.toolConfig.mcpServers;
58
+ }
59
+ getMCPServer(serverId) {
60
+ return this.toolConfig.mcpServers.find((server) => server.id === serverId);
61
+ }
62
+ getMCPServerPrompt(serverId, promptName) {
63
+ const server = this.getMCPServer(serverId);
64
+ if (server) {
65
+ return server.prompts.find((prompt) => prompt.name === promptName);
66
+ }
67
+ return null;
68
+ }
69
+ get mcpServerSettings() {
70
+ return this.capabilities.mcp_server_settings;
71
+ }
56
72
  }
57
73
  class NBIAPI {
58
74
  static async initialize() {
@@ -61,7 +77,10 @@ class NBIAPI {
61
77
  NBIAPI.initializeWebsocket();
62
78
  this._messageReceived.connect((_, msg) => {
63
79
  msg = JSON.parse(msg);
64
- if (msg.type === BackendMessageType.GitHubCopilotLoginStatusChange) {
80
+ if (msg.type === BackendMessageType.MCPServerStatusChange) {
81
+ this.fetchCapabilities();
82
+ }
83
+ else if (msg.type === BackendMessageType.GitHubCopilotLoginStatusChange) {
65
84
  this.updateGitHubLoginStatus().then(() => {
66
85
  this.githubLoginStatusChanged.emit();
67
86
  });
@@ -92,6 +111,21 @@ class NBIAPI {
92
111
  static getDeviceVerificationInfo() {
93
112
  return this._deviceVerificationInfo;
94
113
  }
114
+ static getGHLoginRequired() {
115
+ return (this.config.usingGitHubCopilotModel &&
116
+ NBIAPI.getLoginStatus() === GitHubCopilotLoginStatus.NotLoggedIn);
117
+ }
118
+ static getChatEnabled() {
119
+ return this.config.chatModel.provider === GITHUB_COPILOT_PROVIDER_ID
120
+ ? !this.getGHLoginRequired()
121
+ : this.config.llmProviders.find(provider => provider.id === this.config.chatModel.provider);
122
+ }
123
+ static getInlineCompletionEnabled() {
124
+ return this.config.inlineCompletionModel.provider ===
125
+ GITHUB_COPILOT_PROVIDER_ID
126
+ ? !this.getGHLoginRequired()
127
+ : this.config.llmProviders.find(provider => provider.id === this.config.inlineCompletionModel.provider);
128
+ }
95
129
  static async loginToGitHub() {
96
130
  this._loginStatus = GitHubCopilotLoginStatus.ActivatingDevice;
97
131
  return new Promise((resolve, reject) => {
@@ -144,9 +178,19 @@ class NBIAPI {
144
178
  return new Promise((resolve, reject) => {
145
179
  requestAPI('capabilities', { method: 'GET' })
146
180
  .then(data => {
181
+ const oldConfig = {
182
+ capabilities: structuredClone(this.config.capabilities),
183
+ chatParticipants: structuredClone(this.config.chatParticipants)
184
+ };
147
185
  this.config.capabilities = structuredClone(data);
148
186
  this.config.chatParticipants = structuredClone(data.chat_participants);
149
- this.configChanged.emit();
187
+ const newConfig = {
188
+ capabilities: structuredClone(this.config.capabilities),
189
+ chatParticipants: structuredClone(this.config.chatParticipants)
190
+ };
191
+ if (JSON.stringify(newConfig) !== JSON.stringify(oldConfig)) {
192
+ this.configChanged.emit();
193
+ }
150
194
  resolve();
151
195
  })
152
196
  .catch(reason => {
@@ -183,19 +227,6 @@ class NBIAPI {
183
227
  });
184
228
  });
185
229
  }
186
- static async reloadMCPServerList() {
187
- return new Promise((resolve, reject) => {
188
- requestAPI('reload-mcp-servers', { method: 'POST' })
189
- .then(async (data) => {
190
- await NBIAPI.fetchCapabilities();
191
- resolve(data);
192
- })
193
- .catch(reason => {
194
- console.error(`Failed to reload MCP server list.\n${reason}`);
195
- reject(reason);
196
- });
197
- });
198
- }
199
230
  static async getMCPConfigFile() {
200
231
  return new Promise((resolve, reject) => {
201
232
  requestAPI('mcp-config-file', { method: 'GET' })
@@ -244,6 +275,19 @@ class NBIAPI {
244
275
  }
245
276
  }));
246
277
  }
278
+ static async reloadMCPServers() {
279
+ return new Promise((resolve, reject) => {
280
+ requestAPI('reload-mcp-servers', { method: 'POST' })
281
+ .then(async (data) => {
282
+ await NBIAPI.fetchCapabilities();
283
+ resolve(data);
284
+ })
285
+ .catch(reason => {
286
+ console.error(`Failed to reload MCP servers.\n${reason}`);
287
+ reject(reason);
288
+ });
289
+ });
290
+ }
247
291
  static async generateCode(chatId, prompt, prefix, suffix, existingCode, language, filename, responseEmitter) {
248
292
  const messageId = UUID.uuid4();
249
293
  this._messageReceived.connect((_, msg) => {
@@ -42,6 +42,8 @@ export interface IInlinePromptWidgetOptions {
42
42
  existingCode: string;
43
43
  prefix: string;
44
44
  suffix: string;
45
+ language?: string;
46
+ filename?: string;
45
47
  onRequestSubmitted: (prompt: string) => void;
46
48
  onRequestCancelled: () => void;
47
49
  onContentStream: (content: string) => void;
@@ -73,12 +75,12 @@ export declare class GitHubCopilotLoginDialogBody extends ReactWidget {
73
75
  render(): JSX.Element;
74
76
  private _onLoggedIn;
75
77
  }
76
- export declare class ConfigurationDialogBody extends ReactWidget {
78
+ export declare class FormInputDialogBody extends ReactWidget {
77
79
  constructor(options: {
78
- onSave: () => void;
79
- onEditMCPConfigClicked: () => void;
80
+ fields: any;
81
+ onDone: (formData: any) => void;
80
82
  });
81
83
  render(): JSX.Element;
82
- private _onEditMCPConfigClicked;
83
- private _onSave;
84
+ private _fields;
85
+ private _onDone;
84
86
  }