@jupyterlite/ai 0.9.1 → 0.11.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.
Files changed (71) hide show
  1. package/README.md +5 -214
  2. package/lib/agent.d.ts +58 -66
  3. package/lib/agent.js +291 -310
  4. package/lib/approval-buttons.d.ts +19 -82
  5. package/lib/approval-buttons.js +36 -289
  6. package/lib/chat-model-registry.d.ts +6 -0
  7. package/lib/chat-model-registry.js +4 -1
  8. package/lib/chat-model.d.ts +26 -54
  9. package/lib/chat-model.js +277 -303
  10. package/lib/components/clear-button.d.ts +6 -1
  11. package/lib/components/clear-button.js +10 -6
  12. package/lib/components/completion-status.d.ts +5 -0
  13. package/lib/components/completion-status.js +5 -4
  14. package/lib/components/model-select.d.ts +6 -1
  15. package/lib/components/model-select.js +13 -16
  16. package/lib/components/stop-button.d.ts +6 -1
  17. package/lib/components/stop-button.js +12 -8
  18. package/lib/components/token-usage-display.d.ts +5 -0
  19. package/lib/components/token-usage-display.js +2 -2
  20. package/lib/components/tool-select.d.ts +6 -1
  21. package/lib/components/tool-select.js +10 -9
  22. package/lib/index.d.ts +1 -0
  23. package/lib/index.js +61 -81
  24. package/lib/models/settings-model.d.ts +1 -1
  25. package/lib/models/settings-model.js +40 -26
  26. package/lib/providers/built-in-providers.js +38 -19
  27. package/lib/providers/models.d.ts +3 -3
  28. package/lib/providers/provider-registry.d.ts +3 -4
  29. package/lib/providers/provider-registry.js +1 -4
  30. package/lib/tokens.d.ts +5 -6
  31. package/lib/tools/commands.d.ts +2 -1
  32. package/lib/tools/commands.js +36 -49
  33. package/lib/widgets/ai-settings.d.ts +6 -0
  34. package/lib/widgets/ai-settings.js +72 -71
  35. package/lib/widgets/main-area-chat.d.ts +2 -0
  36. package/lib/widgets/main-area-chat.js +5 -2
  37. package/lib/widgets/provider-config-dialog.d.ts +2 -0
  38. package/lib/widgets/provider-config-dialog.js +34 -34
  39. package/package.json +13 -13
  40. package/schema/settings-model.json +3 -2
  41. package/src/agent.ts +360 -372
  42. package/src/approval-buttons.ts +43 -389
  43. package/src/chat-model-registry.ts +9 -1
  44. package/src/chat-model.ts +399 -370
  45. package/src/completion/completion-provider.ts +2 -3
  46. package/src/components/clear-button.tsx +18 -6
  47. package/src/components/completion-status.tsx +18 -4
  48. package/src/components/model-select.tsx +25 -16
  49. package/src/components/stop-button.tsx +22 -9
  50. package/src/components/token-usage-display.tsx +14 -2
  51. package/src/components/tool-select.tsx +27 -9
  52. package/src/index.ts +78 -134
  53. package/src/models/settings-model.ts +41 -27
  54. package/src/providers/built-in-providers.ts +38 -19
  55. package/src/providers/models.ts +3 -3
  56. package/src/providers/provider-registry.ts +4 -8
  57. package/src/tokens.ts +5 -6
  58. package/src/tools/commands.ts +40 -53
  59. package/src/widgets/ai-settings.tsx +153 -84
  60. package/src/widgets/main-area-chat.ts +8 -2
  61. package/src/widgets/provider-config-dialog.tsx +54 -41
  62. package/style/base.css +24 -73
  63. package/lib/mcp/browser.d.ts +0 -68
  64. package/lib/mcp/browser.js +0 -138
  65. package/lib/tools/file.d.ts +0 -36
  66. package/lib/tools/file.js +0 -351
  67. package/lib/tools/notebook.d.ts +0 -40
  68. package/lib/tools/notebook.js +0 -779
  69. package/src/mcp/browser.ts +0 -220
  70. package/src/tools/file.ts +0 -438
  71. package/src/tools/notebook.ts +0 -986
@@ -25,6 +25,7 @@ import {
25
25
  TextField,
26
26
  Typography
27
27
  } from '@mui/material';
28
+ import type { TranslationBundle } from '@jupyterlab/translation';
28
29
  import React from 'react';
29
30
  import { IProviderConfig, IProviderParameters } from '../models/settings-model';
30
31
  import type { IProviderRegistry } from '../tokens';
@@ -47,6 +48,7 @@ interface IProviderConfigDialogProps {
47
48
  provider: string,
48
49
  fieldName: string
49
50
  ) => Promise<void>;
51
+ trans: TranslationBundle;
50
52
  }
51
53
 
52
54
  export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
@@ -56,7 +58,8 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
56
58
  initialConfig,
57
59
  mode,
58
60
  providerRegistry,
59
- handleSecretField
61
+ handleSecretField,
62
+ trans
60
63
  }) => {
61
64
  const apiKeyRef = React.useRef<HTMLInputElement>();
62
65
  const [name, setName] = React.useState(initialConfig?.name || '');
@@ -158,25 +161,29 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
158
161
  return (
159
162
  <Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
160
163
  <DialogTitle>
161
- {mode === 'add' ? 'Add New Provider' : 'Edit Provider'}
164
+ {mode === 'add'
165
+ ? trans.__('Add New Provider')
166
+ : trans.__('Edit Provider')}
162
167
  </DialogTitle>
163
168
  <DialogContent>
164
169
  <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, pt: 1 }}>
165
170
  <TextField
166
171
  fullWidth
167
- label="Provider Name"
172
+ label={trans.__('Provider Name')}
168
173
  value={name}
169
174
  onChange={e => setName(e.target.value)}
170
- placeholder="e.g., My Anthropic Config, Work Provider"
171
- helperText="A friendly name to identify this provider configuration"
175
+ placeholder={trans.__('e.g., My Anthropic Config, Work Provider')}
176
+ helperText={trans.__(
177
+ 'A friendly name to identify this provider configuration'
178
+ )}
172
179
  required
173
180
  />
174
181
 
175
182
  <FormControl fullWidth required>
176
- <InputLabel>Provider Type</InputLabel>
183
+ <InputLabel>{trans.__('Provider Type')}</InputLabel>
177
184
  <Select
178
185
  value={provider}
179
- label="Provider Type"
186
+ label={trans.__('Provider Type')}
180
187
  onChange={e =>
181
188
  setProvider(e.target.value as IProviderConfig['provider'])
182
189
  }
@@ -189,7 +196,7 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
189
196
  {option.apiKeyRequirement === 'required' && (
190
197
  <Chip
191
198
  size="small"
192
- label="API Key"
199
+ label={trans.__('API Key')}
193
200
  color="default"
194
201
  variant="outlined"
195
202
  />
@@ -209,19 +216,19 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
209
216
  {selectedProvider?.allowCustomModel ? (
210
217
  <TextField
211
218
  fullWidth
212
- label="Model"
219
+ label={trans.__('Model')}
213
220
  value={model}
214
221
  onChange={e => setModel(e.target.value)}
215
- placeholder="Enter model name"
216
- helperText="Enter any compatible model name"
222
+ placeholder={trans.__('Enter model name')}
223
+ helperText={trans.__('Enter any compatible model name')}
217
224
  required
218
225
  />
219
226
  ) : (
220
227
  <FormControl fullWidth required>
221
- <InputLabel>Model</InputLabel>
228
+ <InputLabel>{trans.__('Model')}</InputLabel>
222
229
  <Select
223
230
  value={model}
224
- label="Model"
231
+ label={trans.__('Model')}
225
232
  onChange={e => setModel(e.target.value)}
226
233
  >
227
234
  {selectedProvider?.models.map(modelOption => (
@@ -230,18 +237,18 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
230
237
  <Typography variant="body1">{modelOption}</Typography>
231
238
  <Typography variant="caption" color="text.secondary">
232
239
  {modelOption.includes('sonnet')
233
- ? 'Balanced performance'
240
+ ? trans.__('Balanced performance')
234
241
  : modelOption.includes('opus')
235
- ? 'Advanced reasoning'
242
+ ? trans.__('Advanced reasoning')
236
243
  : modelOption.includes('haiku')
237
- ? 'Fast and lightweight'
244
+ ? trans.__('Fast and lightweight')
238
245
  : modelOption.includes('large')
239
- ? 'Most capable model'
246
+ ? trans.__('Most capable model')
240
247
  : modelOption.includes('small')
241
- ? 'Fast and efficient'
248
+ ? trans.__('Fast and efficient')
242
249
  : modelOption.includes('codestral')
243
- ? 'Code-specialized'
244
- : 'General purpose'}
250
+ ? trans.__('Code-specialized')
251
+ : trans.__('General purpose')}
245
252
  </Typography>
246
253
  </Box>
247
254
  </MenuItem>
@@ -257,13 +264,13 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
257
264
  inputRef={apiKeyRef}
258
265
  label={
259
266
  selectedProvider?.apiKeyRequirement === 'required'
260
- ? 'API Key'
261
- : 'API Key (Optional)'
267
+ ? trans.__('API Key')
268
+ : trans.__('API Key (Optional)')
262
269
  }
263
270
  type={showApiKey ? 'text' : 'password'}
264
271
  value={apiKey}
265
272
  onChange={e => setApiKey(e.target.value)}
266
- placeholder="Enter your API key..."
273
+ placeholder={trans.__('Enter your API key...')}
267
274
  required={selectedProvider?.apiKeyRequirement === 'required'}
268
275
  InputProps={{
269
276
  endAdornment: (
@@ -315,7 +322,7 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
315
322
  <TextField
316
323
  {...params}
317
324
  fullWidth
318
- label="Base URL"
325
+ label={trans.__('Base URL')}
319
326
  placeholder="https://api.example.com/v1"
320
327
  onChange={e => setBaseURL(e.target.value)}
321
328
  />
@@ -339,14 +346,17 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
339
346
  >
340
347
  <AccordionSummary expandIcon={<ExpandMore />}>
341
348
  <Typography variant="subtitle1" fontWeight="medium">
342
- Advanced Settings
349
+ {trans.__('Advanced Settings')}
343
350
  </Typography>
344
351
  </AccordionSummary>
345
352
  <AccordionDetails sx={{ bgcolor: 'transparent' }}>
346
353
  <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
347
354
  <Box>
348
355
  <Typography gutterBottom>
349
- Temperature: {parameters.temperature ?? 'Default'}
356
+ {trans.__(
357
+ 'Temperature: %1',
358
+ parameters.temperature ?? trans.__('Default')
359
+ )}
350
360
  </Typography>
351
361
  <Slider
352
362
  value={parameters.temperature ?? DEFAULT_TEMPERATURE}
@@ -362,32 +372,33 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
362
372
  valueLabelDisplay="auto"
363
373
  />
364
374
  <Typography variant="caption" color="text.secondary">
365
- Temperature for the model (lower values are more
366
- deterministic)
375
+ {trans.__(
376
+ 'Temperature for the model (lower values are more deterministic)'
377
+ )}
367
378
  </Typography>
368
379
  </Box>
369
380
 
370
381
  <TextField
371
382
  fullWidth
372
- label="Max Tokens (Optional)"
383
+ label={trans.__('Max Tokens (Optional)')}
373
384
  type="number"
374
- value={parameters.maxTokens ?? ''}
385
+ value={parameters.maxOutputTokens ?? ''}
375
386
  onChange={e =>
376
387
  setParameters({
377
388
  ...parameters,
378
- maxTokens: e.target.value
389
+ maxOutputTokens: e.target.value
379
390
  ? Number(e.target.value)
380
391
  : undefined
381
392
  })
382
393
  }
383
- placeholder="Leave empty for provider default"
384
- helperText="Maximum length of AI responses"
394
+ placeholder={trans.__('Leave empty for provider default')}
395
+ helperText={trans.__('Maximum length of AI responses')}
385
396
  inputProps={{ min: 1 }}
386
397
  />
387
398
 
388
399
  <TextField
389
400
  fullWidth
390
- label="Max Turns (Optional)"
401
+ label={trans.__('Max Turns (Optional)')}
391
402
  type="number"
392
403
  value={parameters.maxTurns ?? ''}
393
404
  onChange={e =>
@@ -398,8 +409,10 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
398
409
  : undefined
399
410
  })
400
411
  }
401
- placeholder={`Default: ${DEFAULT_MAX_TURNS}`}
402
- helperText="Maximum number of tool execution turns"
412
+ placeholder={trans.__('Default: %1', DEFAULT_MAX_TURNS)}
413
+ helperText={trans.__(
414
+ 'Maximum number of tool execution turns'
415
+ )}
403
416
  inputProps={{ min: 1, max: 100 }}
404
417
  />
405
418
 
@@ -408,7 +421,7 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
408
421
  color="text.secondary"
409
422
  sx={{ mt: 2, mb: 1 }}
410
423
  >
411
- Completion Options
424
+ {trans.__('Completion Options')}
412
425
  </Typography>
413
426
 
414
427
  <FormControlLabel
@@ -423,7 +436,7 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
423
436
  }
424
437
  />
425
438
  }
426
- label="Fill-in-the-middle support"
439
+ label={trans.__('Fill-in-the-middle support')}
427
440
  />
428
441
 
429
442
  <FormControlLabel
@@ -438,7 +451,7 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
438
451
  }
439
452
  />
440
453
  }
441
- label="Use filter text"
454
+ label={trans.__('Use filter text')}
442
455
  />
443
456
  </Box>
444
457
  </AccordionDetails>
@@ -446,9 +459,9 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
446
459
  </Box>
447
460
  </DialogContent>
448
461
  <DialogActions>
449
- <Button onClick={onClose}>Cancel</Button>
462
+ <Button onClick={onClose}>{trans.__('Cancel')}</Button>
450
463
  <Button onClick={handleSave} variant="contained" disabled={!isValid}>
451
- {mode === 'add' ? 'Add Provider' : 'Save Changes'}
464
+ {mode === 'add' ? trans.__('Add Provider') : trans.__('Save Changes')}
452
465
  </Button>
453
466
  </DialogActions>
454
467
  </Dialog>
package/style/base.css CHANGED
@@ -74,7 +74,7 @@
74
74
  /* Enhanced tool select menu styles */
75
75
  .jp-AIToolSelect-menu {
76
76
  max-width: 300px;
77
- box-shadow: 0 4px 12px rgb(0 0 0 / 15%);
77
+ box-shadow: var(--jp-elevation-z6);
78
78
  border: 1px solid var(--jp-border-color2);
79
79
  }
80
80
 
@@ -94,14 +94,14 @@
94
94
  border: 1px solid var(--jp-border-color1);
95
95
  border-radius: 6px;
96
96
  background: var(--jp-layout-color0);
97
- box-shadow: 0 1px 3px rgb(0 0 0 / 8%);
97
+ box-shadow: var(--jp-elevation-z2);
98
98
  transition: all 0.2s ease;
99
99
  overflow: hidden;
100
100
  }
101
101
 
102
102
  .jp-ai-tool-call:hover {
103
103
  border-color: var(--jp-border-color2);
104
- box-shadow: 0 2px 6px rgb(0 0 0 / 12%);
104
+ box-shadow: var(--jp-elevation-z4);
105
105
  }
106
106
 
107
107
  /* Tool Header - clickable summary */
@@ -151,6 +151,17 @@
151
151
  flex: 1;
152
152
  }
153
153
 
154
+ .jp-ai-tool-summary {
155
+ font-weight: 400;
156
+ opacity: 0.7;
157
+ font-size: var(--jp-ui-font-size0);
158
+ }
159
+
160
+ .jp-ai-tool-summary::before {
161
+ content: ' ';
162
+ white-space: pre;
163
+ }
164
+
154
165
  .jp-ai-tool-status {
155
166
  font-size: var(--jp-ui-font-size0);
156
167
  font-weight: 500;
@@ -173,6 +184,11 @@
173
184
  color: var(--jp-error-color1);
174
185
  }
175
186
 
187
+ .jp-ai-tool-status-approval {
188
+ background: rgb(var(--jp-warn-color1-rgb) / 15%);
189
+ color: var(--jp-warn-color1);
190
+ }
191
+
176
192
  /* Tool Body */
177
193
  .jp-ai-tool-body {
178
194
  padding: 8px 12px 12px;
@@ -251,40 +267,7 @@
251
267
  padding: 0 0.4em;
252
268
  }
253
269
 
254
- /* Tool Approval UI Styles */
255
- .jp-ai-tool-approval {
256
- margin: 8px 0;
257
- border: 2px solid var(--jp-warn-color1);
258
- border-radius: 8px;
259
- background: var(--jp-layout-color0);
260
- box-shadow: 0 2px 8px rgb(0 0 0 / 12%);
261
- overflow: hidden;
262
- }
263
-
264
- .jp-ai-tool-approval-header {
265
- display: flex;
266
- align-items: center;
267
- padding: 12px 16px;
268
- background: rgb(var(--jp-warn-color1-rgb) / 8%);
269
- gap: 12px;
270
- }
271
-
272
- .jp-ai-tool-approval-header .jp-ai-tool-icon {
273
- font-size: 16px;
274
- }
275
-
276
- .jp-ai-tool-approval-header .jp-ai-tool-title {
277
- font-family: var(--jp-ui-font-family);
278
- font-size: var(--jp-ui-font-size1);
279
- font-weight: 600;
280
- color: var(--jp-warn-color1);
281
- flex: 1;
282
- }
283
-
284
- .jp-ai-tool-approval-body {
285
- padding: 16px;
286
- }
287
-
270
+ /* Tool Approval Button Styles */
288
271
  .jp-ai-tool-approval-buttons,
289
272
  .jp-ai-group-approval-buttons {
290
273
  display: flex;
@@ -308,24 +291,24 @@
308
291
 
309
292
  .jp-ai-approval-approve {
310
293
  background: var(--jp-success-color1);
311
- color: white;
294
+ color: var(--jp-ui-inverse-font-color1);
312
295
  }
313
296
 
314
297
  .jp-ai-approval-approve:hover:not(:disabled) {
315
298
  background: var(--jp-success-color0);
316
299
  transform: translateY(-1px);
317
- box-shadow: 0 2px 4px rgb(0 0 0 / 15%);
300
+ box-shadow: var(--jp-elevation-z4);
318
301
  }
319
302
 
320
303
  .jp-ai-approval-reject {
321
304
  background: var(--jp-error-color1);
322
- color: white;
305
+ color: var(--jp-ui-inverse-font-color1);
323
306
  }
324
307
 
325
308
  .jp-ai-approval-reject:hover:not(:disabled) {
326
309
  background: var(--jp-error-color0);
327
310
  transform: translateY(-1px);
328
- box-shadow: 0 2px 4px rgb(0 0 0 / 15%);
311
+ box-shadow: var(--jp-elevation-z4);
329
312
  }
330
313
 
331
314
  .jp-ai-approval-btn:disabled {
@@ -333,38 +316,6 @@
333
316
  opacity: 0.5;
334
317
  }
335
318
 
336
- /* Unified Approval Status Styles for both single and grouped tools */
337
- .jp-ai-approval-status,
338
- .jp-ai-group-approval-status {
339
- display: flex;
340
- align-items: center;
341
- justify-content: center;
342
- gap: 8px;
343
- padding: 8px 16px;
344
- border-radius: 4px;
345
- font-family: var(--jp-ui-font-family);
346
- font-size: var(--jp-ui-font-size1);
347
- font-weight: 500;
348
- }
349
-
350
- .jp-ai-approval-status-approved,
351
- .jp-ai-group-approval-status-approved {
352
- background: rgb(var(--jp-success-color1-rgb) / 15%);
353
- color: var(--jp-success-color1);
354
- border: 1px solid rgb(var(--jp-success-color1-rgb) / 30%);
355
- }
356
-
357
- .jp-ai-approval-status-rejected,
358
- .jp-ai-group-approval-status-rejected {
359
- background: rgb(var(--jp-error-color1-rgb) / 15%);
360
- color: var(--jp-error-color1);
361
- border: 1px solid rgb(var(--jp-error-color1-rgb) / 30%);
362
- }
363
-
364
- .jp-ai-approval-icon {
365
- font-size: 16px;
366
- }
367
-
368
319
  .jp-MainAreaWidget
369
320
  .jp-ToolbarButtonComponent[data-command='@jupyterlite/ai:move-chat']
370
321
  svg {
@@ -1,68 +0,0 @@
1
- /**
2
- * Browser-compatible MCP Server implementation
3
- *
4
- * This is a custom implementation that works around the limitation in
5
- * @openai/agents where MCPServerStreamableHttp doesn't work in browsers
6
- */
7
- interface MCPServer {
8
- cacheToolsList: boolean;
9
- toolFilter?: any;
10
- connect(): Promise<void>;
11
- readonly name: string;
12
- close(): Promise<void>;
13
- listTools(): Promise<MCPTool[]>;
14
- callTool(toolName: string, args: Record<string, unknown> | null): Promise<CallToolResultContent>;
15
- invalidateToolsCache(): Promise<void>;
16
- }
17
- interface MCPTool {
18
- name: string;
19
- description?: string;
20
- inputSchema: {
21
- type: 'object';
22
- properties: Record<string, any>;
23
- required: string[];
24
- additionalProperties: boolean;
25
- };
26
- }
27
- type CallToolResultContent = Array<{
28
- type: string;
29
- text: string;
30
- }>;
31
- interface MCPServerStreamableHttpOptions {
32
- url: string;
33
- cacheToolsList?: boolean;
34
- clientSessionTimeoutSeconds?: number;
35
- name?: string;
36
- logger?: any;
37
- toolFilter?: any;
38
- timeout?: number;
39
- authProvider?: any;
40
- requestInit?: any;
41
- fetch?: any;
42
- reconnectionOptions?: any;
43
- sessionId?: string;
44
- }
45
- /**
46
- * Browser-compatible MCP Server implementation that works around limitations
47
- * in @openai/agents where MCPServerStreamableHttp doesn't work in browsers.
48
- *
49
- * This class provides a streamable HTTP client transport for MCP (Model Context Protocol)
50
- * servers that can be used in browser environments.
51
- */
52
- export declare class BrowserMCPServerStreamableHttp implements MCPServer {
53
- readonly name: string;
54
- readonly cacheToolsList: boolean;
55
- readonly toolFilter: any;
56
- constructor(options: MCPServerStreamableHttpOptions);
57
- connect(): Promise<void>;
58
- close(): Promise<void>;
59
- listTools(): Promise<MCPTool[]>;
60
- callTool(toolName: string, args: Record<string, unknown> | null): Promise<CallToolResultContent>;
61
- invalidateToolsCache(): Promise<void>;
62
- private _session;
63
- private _toolsList;
64
- private _cacheDirty;
65
- private _transport;
66
- private _options;
67
- }
68
- export {};
@@ -1,138 +0,0 @@
1
- /* eslint-disable @typescript-eslint/naming-convention */
2
- /**
3
- * Browser-compatible MCP Server implementation
4
- *
5
- * This is a custom implementation that works around the limitation in
6
- * @openai/agents where MCPServerStreamableHttp doesn't work in browsers
7
- */
8
- /**
9
- * Browser-compatible MCP Server implementation that works around limitations
10
- * in @openai/agents where MCPServerStreamableHttp doesn't work in browsers.
11
- *
12
- * This class provides a streamable HTTP client transport for MCP (Model Context Protocol)
13
- * servers that can be used in browser environments.
14
- */
15
- export class BrowserMCPServerStreamableHttp {
16
- name;
17
- cacheToolsList;
18
- toolFilter = undefined;
19
- constructor(options) {
20
- this._options = options;
21
- this.name = options.name || `browser-mcp-server: ${options.url}`;
22
- this.cacheToolsList = options.cacheToolsList ?? false;
23
- }
24
- async connect() {
25
- try {
26
- // Dynamic import to handle cases where MCP SDK isn't available
27
- const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js');
28
- const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
29
- // Merge CORS-enabled requestInit with user options
30
- const corsRequestInit = {
31
- mode: 'cors',
32
- credentials: 'omit',
33
- ...this._options.requestInit
34
- };
35
- this._transport = new StreamableHTTPClientTransport(new URL(this._options.url), {
36
- authProvider: this._options.authProvider,
37
- requestInit: corsRequestInit,
38
- fetch: this._options.fetch || fetch,
39
- reconnectionOptions: this._options.reconnectionOptions,
40
- sessionId: this._options.sessionId
41
- });
42
- this._session = new Client({
43
- name: this.name,
44
- version: '1.0.0'
45
- });
46
- await this._session.connect(this._transport);
47
- }
48
- catch (error) {
49
- console.error('Error initializing MCP server:', error);
50
- await this.close();
51
- throw error;
52
- }
53
- }
54
- async close() {
55
- if (this._session) {
56
- try {
57
- await this._session.close();
58
- }
59
- catch (error) {
60
- console.error('Error closing MCP server session:', error);
61
- }
62
- this._session = null;
63
- }
64
- if (this._transport) {
65
- try {
66
- await this._transport.close();
67
- }
68
- catch (error) {
69
- console.error('Error closing MCP server transport:', error);
70
- }
71
- this._transport = null;
72
- }
73
- }
74
- async listTools() {
75
- if (!this._session) {
76
- throw new Error('Server not initialized. Call connect() first.');
77
- }
78
- if (this.cacheToolsList &&
79
- !this._cacheDirty &&
80
- this._toolsList.length > 0) {
81
- return this._toolsList;
82
- }
83
- try {
84
- const { ListToolsResultSchema } = await import('@modelcontextprotocol/sdk/types.js');
85
- const response = await this._session.listTools();
86
- const parsedResponse = ListToolsResultSchema.parse(response);
87
- // Map to openai/agents MCPTool type
88
- this._toolsList = parsedResponse.tools.map((tool) => ({
89
- name: tool.name,
90
- description: tool.description,
91
- inputSchema: {
92
- type: 'object',
93
- properties: tool.inputSchema?.properties || {},
94
- required: tool.inputSchema?.required || [],
95
- additionalProperties: tool.inputSchema?.additionalProperties ?? false
96
- }
97
- }));
98
- this._cacheDirty = false;
99
- return this._toolsList;
100
- }
101
- catch (error) {
102
- console.error(`Error listing tools from ${this.name}:`, error);
103
- throw error;
104
- }
105
- }
106
- async callTool(toolName, args) {
107
- if (!this._session) {
108
- throw new Error('Server not initialized. Call connect() first.');
109
- }
110
- try {
111
- const { CallToolResultSchema } = await import('@modelcontextprotocol/sdk/types.js');
112
- const response = await this._session.callTool({
113
- name: toolName,
114
- arguments: args ?? {}
115
- }, undefined, {
116
- timeout: this._options.timeout ?? 30000
117
- });
118
- // Parse and validate using MCP SDK schema
119
- const parsed = CallToolResultSchema.parse(response);
120
- const result = parsed.content;
121
- // Return the content array as expected by openai/agents
122
- // CallToolResultContent is { type: string; text: string }[]
123
- return result;
124
- }
125
- catch (error) {
126
- console.error(`Error calling tool ${toolName}:`, error);
127
- throw error;
128
- }
129
- }
130
- async invalidateToolsCache() {
131
- this._cacheDirty = true;
132
- }
133
- _session = null;
134
- _toolsList = [];
135
- _cacheDirty = true;
136
- _transport = null;
137
- _options;
138
- }
@@ -1,36 +0,0 @@
1
- import { CommandRegistry } from '@lumino/commands';
2
- import { IDocumentManager } from '@jupyterlab/docmanager';
3
- import { IEditorTracker } from '@jupyterlab/fileeditor';
4
- import { IDiffManager, ITool } from '../tokens';
5
- /**
6
- * Create a tool for creating new files of various types
7
- */
8
- export declare function createNewFileTool(docManager: IDocumentManager): ITool;
9
- /**
10
- * Create a tool for opening files
11
- */
12
- export declare function createOpenFileTool(docManager: IDocumentManager): ITool;
13
- /**
14
- * Create a tool for deleting files
15
- */
16
- export declare function createDeleteFileTool(docManager: IDocumentManager): ITool;
17
- /**
18
- * Create a tool for renaming files
19
- */
20
- export declare function createRenameFileTool(docManager: IDocumentManager): ITool;
21
- /**
22
- * Create a tool for copying files
23
- */
24
- export declare function createCopyFileTool(docManager: IDocumentManager): ITool;
25
- /**
26
- * Create a tool for navigating to directories in the file browser
27
- */
28
- export declare function createNavigateToDirectoryTool(commands: CommandRegistry): ITool;
29
- /**
30
- * Create a tool for getting file information and content
31
- */
32
- export declare function createGetFileInfoTool(docManager: IDocumentManager, editorTracker?: IEditorTracker): ITool;
33
- /**
34
- * Create a tool for setting the content of a file
35
- */
36
- export declare function createSetFileContentTool(docManager: IDocumentManager, diffManager?: IDiffManager): ITool;