@agenticmail/enterprise 0.5.244 → 0.5.246

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.
@@ -21,13 +21,20 @@ export function HelpButton(props) {
21
21
  onMouseEnter: function(e) { e.currentTarget.style.borderColor = 'var(--brand-color, #6366f1)'; e.currentTarget.style.color = 'var(--brand-color, #6366f1)'; },
22
22
  onMouseLeave: function(e) { e.currentTarget.style.borderColor = 'var(--text-muted, #6b7280)'; e.currentTarget.style.color = 'var(--text-muted, #6b7280)'; }
23
23
  }, '?'),
24
- isOpen && h(Modal, {
25
- title: props.label || 'Help',
26
- onClose: function() { setOpen(false); },
27
- large: true
24
+ isOpen && h('div', {
25
+ style: { position: 'fixed', inset: 0, background: 'var(--bg-modal, rgba(0,0,0,0.7))', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 200, animation: 'fadeIn 150ms ease' },
26
+ onClick: function(e) { if (e.target === e.currentTarget) setOpen(false); }
28
27
  },
29
- h('div', { style: { fontSize: 14, lineHeight: 1.7, color: 'var(--text-secondary, #9ca3af)', padding: '4px 0' } },
30
- props.children
28
+ h('div', { className: 'modal', style: { zIndex: 201 } },
29
+ h('div', { className: 'modal-header' },
30
+ h('h2', null, props.label || 'Help'),
31
+ h('button', { className: 'btn btn-ghost btn-icon', onClick: function() { setOpen(false); } }, I.x())
32
+ ),
33
+ h('div', { className: 'modal-body' },
34
+ h('div', { style: { fontSize: 14, lineHeight: 1.7, color: 'var(--text-secondary, #9ca3af)', padding: '4px 0' } },
35
+ props.children
36
+ )
37
+ )
31
38
  )
32
39
  )
33
40
  );
@@ -248,7 +248,10 @@ function McpServersSection() {
248
248
  h('div', { style: { display: 'flex', flexDirection: 'column', gap: 14 } },
249
249
  // Name
250
250
  h('div', { className: 'form-group' },
251
- h('label', { className: 'form-label' }, 'Server Name *'),
251
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Server Name *', h(HelpButton, { label: 'Server Name' },
252
+ h('p', null, 'A friendly name so you can identify this server in the list. Use something descriptive like "GitHub Tools" or "Company Database".'),
253
+ h('p', null, 'This name is only for your reference — it doesn\'t affect how the server works.')
254
+ )),
252
255
  h('input', { className: 'input', placeholder: 'e.g., GitHub MCP, Filesystem, Database', value: form.name,
253
256
  onChange: function(e) { setForm(Object.assign({}, form, { name: e.target.value })); } })
254
257
  ),
@@ -260,7 +263,16 @@ function McpServersSection() {
260
263
  ),
261
264
  // Type selector
262
265
  h('div', { className: 'form-group' },
263
- h('label', { className: 'form-label' }, 'Connection Type *'),
266
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Connection Type *', h(HelpButton, { label: 'Connection Type' },
267
+ h('p', null, 'How your agents communicate with this MCP server. If you\'re not sure, choose ', h('strong', null, 'Local Process'), ' — it\'s the most common and works out of the box.'),
268
+ h('ul', { style: { paddingLeft: 20, margin: '8px 0' } },
269
+ h('li', null, h('strong', null, 'Local Process'), ' — The server runs as a program on the same machine. Best for most use cases. Just provide the command to start it (like "npx" or "docker").'),
270
+ h('li', null, h('strong', null, 'SSE'), ' — Connects to an MCP server hosted elsewhere (another machine or cloud). Uses a live streaming connection.'),
271
+ h('li', null, h('strong', null, 'HTTP'), ' — Connects to a remote MCP server using simple web requests. Used by some cloud-hosted MCP services.')
272
+ ),
273
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
274
+ h('strong', null, 'Not sure? '), 'Use a Quick Start Template below — it pre-fills everything for you.')
275
+ )),
264
276
  h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8 } },
265
277
  ['stdio', 'sse', 'http'].map(function(t) {
266
278
  var selected = form.type === t;
@@ -275,7 +287,7 @@ function McpServersSection() {
275
287
  },
276
288
  h('div', { style: { fontWeight: 600, fontSize: 12 } }, t === 'stdio' ? 'Local Process' : t === 'sse' ? 'SSE' : 'HTTP'),
277
289
  h('div', { style: { fontSize: 10, color: 'var(--text-muted)', marginTop: 2 } },
278
- t === 'stdio' ? 'stdin/stdout' : t === 'sse' ? 'Server-Sent Events' : 'Streamable HTTP')
290
+ t === 'stdio' ? 'Runs on this machine' : t === 'sse' ? 'Remote with live stream' : 'Remote web requests')
279
291
  );
280
292
  })
281
293
  )
@@ -283,56 +295,110 @@ function McpServersSection() {
283
295
  // stdio fields
284
296
  form.type === 'stdio' && h(Fragment, null,
285
297
  h('div', { className: 'form-group' },
286
- h('label', { className: 'form-label' }, 'Command *'),
298
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Command *', h(HelpButton, { label: 'Command' },
299
+ h('p', null, 'The program to run on your server. This is like typing a command in the terminal. Common examples:'),
300
+ h('ul', { style: { paddingLeft: 20, margin: '8px 0' } },
301
+ h('li', null, h('strong', null, 'npx'), ' — Runs Node.js packages without installing them first. Most MCP servers use this.'),
302
+ h('li', null, h('strong', null, 'node'), ' — Runs a JavaScript file directly.'),
303
+ h('li', null, h('strong', null, 'python'), ' or ', h('strong', null, 'python3'), ' — Runs Python-based MCP servers.'),
304
+ h('li', null, h('strong', null, 'docker'), ' — Runs the server inside a Docker container (advanced).')
305
+ ),
306
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
307
+ h('strong', null, 'Tip: '), 'If you chose a Quick Start Template, this is already filled in. Most templates use "npx" which requires Node.js to be installed on your machine.')
308
+ )),
287
309
  h('input', { className: 'input', placeholder: 'npx, node, python, docker...', value: form.command,
288
310
  onChange: function(e) { setForm(Object.assign({}, form, { command: e.target.value })); } }),
289
- h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'The executable to run. Must be installed on this machine.')
311
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'The program to start the MCP server. Must be installed on this machine.')
290
312
  ),
291
313
  h('div', { className: 'form-group' },
292
- h('label', { className: 'form-label' }, 'Arguments'),
293
- h('input', { className: 'input', placeholder: '@modelcontextprotocol/server-filesystem /home/user/docs', value: form.args,
314
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Arguments', h(HelpButton, { label: 'Arguments' },
315
+ h('p', null, 'Extra instructions passed to the command. Think of it like telling the program what to do. For example:'),
316
+ h('ul', { style: { paddingLeft: 20, margin: '8px 0' } },
317
+ h('li', null, h('strong', null, '-y @modelcontextprotocol/server-filesystem /home'), ' — Tells npx to run the filesystem MCP server and give it access to /home'),
318
+ h('li', null, h('strong', null, '-y @modelcontextprotocol/server-github'), ' — Tells npx to run the GitHub MCP server')
319
+ ),
320
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
321
+ h('strong', null, 'Tip: '), 'The "-y" flag tells npx to automatically install the package if needed, without asking. Always include it for npx commands.')
322
+ )),
323
+ h('input', { className: 'input', placeholder: '-y @modelcontextprotocol/server-filesystem /home/user/docs', value: form.args,
294
324
  onChange: function(e) { setForm(Object.assign({}, form, { args: e.target.value })); } }),
295
- h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Space-separated arguments passed to the command.')
325
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Additional options passed to the command. Separate multiple values with spaces.')
296
326
  ),
297
327
  h('div', { className: 'form-group' },
298
- h('label', { className: 'form-label' }, 'Environment Variables'),
328
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Environment Variables', h(HelpButton, { label: 'Environment Variables' },
329
+ h('p', null, 'Some MCP servers need passwords, API keys, or settings to work. Environment variables are a secure way to pass this information.'),
330
+ h('p', { style: { marginTop: 8 } }, 'Format: a JSON object where each key is the variable name and each value is the secret. For example:'),
331
+ h('pre', { style: { background: 'var(--bg-secondary)', padding: 10, borderRadius: 8, fontSize: 12, marginTop: 8 } },
332
+ '{\n "GITHUB_TOKEN": "ghp_abc123...",\n "DATABASE_URL": "postgres://user:pass@host/db"\n}'
333
+ ),
334
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
335
+ h('strong', null, 'Security: '), 'All values are encrypted before being stored. They\'re only decrypted when the server starts.')
336
+ )),
299
337
  h('textarea', { className: 'input', rows: 3, placeholder: '{\n "GITHUB_TOKEN": "ghp_...",\n "DATABASE_URL": "postgres://..."\n}', value: form.env,
300
338
  style: { fontFamily: 'var(--font-mono, monospace)', fontSize: 12 },
301
339
  onChange: function(e) { setForm(Object.assign({}, form, { env: e.target.value })); } }),
302
- h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'JSON object of environment variables. Secrets are encrypted at rest.')
340
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'API keys, tokens, and passwords needed by this server. Stored encrypted.')
303
341
  )
304
342
  ),
305
343
  // HTTP/SSE fields
306
344
  form.type !== 'stdio' && h(Fragment, null,
307
345
  h('div', { className: 'form-group' },
308
- h('label', { className: 'form-label' }, 'Server URL *'),
346
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Server URL *', h(HelpButton, { label: 'Server URL' },
347
+ h('p', null, 'The web address of the remote MCP server. This is provided by whoever hosts the server.'),
348
+ h('p', { style: { marginTop: 8 } }, 'It usually looks like:'),
349
+ h('ul', { style: { paddingLeft: 20, margin: '4px 0' } },
350
+ h('li', null, h('strong', null, 'SSE: '), 'https://mcp.example.com/sse'),
351
+ h('li', null, h('strong', null, 'HTTP: '), 'https://mcp.example.com/mcp')
352
+ ),
353
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
354
+ h('strong', null, 'Tip: '), 'Check the MCP server\'s documentation for the exact URL to use. If it\'s hosted on your local network, use the internal IP (e.g., http://192.168.1.100:3000/mcp).')
355
+ )),
309
356
  h('input', { className: 'input', placeholder: form.type === 'sse' ? 'https://mcp.example.com/sse' : 'https://mcp.example.com/mcp', value: form.url,
310
- onChange: function(e) { setForm(Object.assign({}, form, { url: e.target.value })); } })
357
+ onChange: function(e) { setForm(Object.assign({}, form, { url: e.target.value })); } }),
358
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'The web address where the MCP server is running.')
311
359
  ),
312
360
  h('div', { className: 'form-group' },
313
- h('label', { className: 'form-label' }, 'API Key / Bearer Token'),
314
- h('input', { className: 'input', type: 'password', placeholder: 'Optional for authenticated endpoints', value: form.apiKey,
315
- onChange: function(e) { setForm(Object.assign({}, form, { apiKey: e.target.value })); } })
361
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'API Key / Bearer Token', h(HelpButton, { label: 'API Key' },
362
+ h('p', null, 'If the remote server requires authentication, paste the API key or token here. This is like a password that proves you\'re allowed to use the server.'),
363
+ h('p', { style: { marginTop: 8 } }, 'You usually get this from the MCP server provider\'s dashboard or settings page. If the server is open/public, leave this empty.')
364
+ )),
365
+ h('input', { className: 'input', type: 'password', placeholder: 'Optional — only if the server requires authentication', value: form.apiKey,
366
+ onChange: function(e) { setForm(Object.assign({}, form, { apiKey: e.target.value })); } }),
367
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Only needed if the server requires authentication. Leave empty for open servers.')
316
368
  ),
317
369
  h('div', { className: 'form-group' },
318
- h('label', { className: 'form-label' }, 'Custom Headers'),
370
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Custom Headers', h(HelpButton, { label: 'Custom Headers' },
371
+ h('p', null, 'Advanced setting. Some servers need extra information sent with every request (like an organization ID or custom authentication format).'),
372
+ h('p', { style: { marginTop: 8 } }, 'Most users can leave this empty. If you need it, your MCP server provider will tell you what to put here.'),
373
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
374
+ h('strong', null, 'Tip: '), 'This is rarely needed. If you\'re not sure, skip it.')
375
+ )),
319
376
  h('textarea', { className: 'input', rows: 2, placeholder: '{\n "X-Custom-Header": "value"\n}', value: form.headers,
320
377
  style: { fontFamily: 'var(--font-mono, monospace)', fontSize: 12 },
321
- onChange: function(e) { setForm(Object.assign({}, form, { headers: e.target.value })); } })
378
+ onChange: function(e) { setForm(Object.assign({}, form, { headers: e.target.value })); } }),
379
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Advanced. Usually not needed — leave empty unless told otherwise.')
322
380
  )
323
381
  ),
324
382
  // Common settings
325
383
  h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 } },
326
384
  h('div', { className: 'form-group' },
327
- h('label', { className: 'form-label' }, 'Connection Timeout (s)'),
385
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Connection Timeout', h(HelpButton, { label: 'Timeout' },
386
+ h('p', null, 'How long to wait (in seconds) for the server to respond before giving up. The default of 30 seconds works for most servers.'),
387
+ h('p', { style: { marginTop: 8 } }, 'Increase this if the server is slow to start (e.g., Docker containers) or on a slow network.')
388
+ )),
328
389
  h('input', { className: 'input', type: 'number', min: 5, max: 300, value: form.timeout,
329
- onChange: function(e) { setForm(Object.assign({}, form, { timeout: e.target.value })); } })
390
+ onChange: function(e) { setForm(Object.assign({}, form, { timeout: e.target.value })); } }),
391
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Seconds to wait. 30 is usually fine.')
330
392
  ),
331
393
  h('div', { className: 'form-group' },
332
394
  h('label', { style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, marginTop: 24 } },
333
395
  h('input', { type: 'checkbox', checked: form.autoRestart,
334
396
  onChange: function(e) { setForm(Object.assign({}, form, { autoRestart: e.target.checked })); } }),
335
- 'Auto-restart on failure'
397
+ 'Auto-restart on failure',
398
+ h(HelpButton, { label: 'Auto-restart' },
399
+ h('p', null, 'If the MCP server crashes or stops unexpectedly, we\'ll automatically restart it so your agents don\'t lose access to its tools.'),
400
+ h('p', { style: { marginTop: 8 } }, 'Keep this on unless you have a reason to disable it.')
401
+ )
336
402
  )
337
403
  )
338
404
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/enterprise",
3
- "version": "0.5.244",
3
+ "version": "0.5.246",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,13 +21,20 @@ export function HelpButton(props) {
21
21
  onMouseEnter: function(e) { e.currentTarget.style.borderColor = 'var(--brand-color, #6366f1)'; e.currentTarget.style.color = 'var(--brand-color, #6366f1)'; },
22
22
  onMouseLeave: function(e) { e.currentTarget.style.borderColor = 'var(--text-muted, #6b7280)'; e.currentTarget.style.color = 'var(--text-muted, #6b7280)'; }
23
23
  }, '?'),
24
- isOpen && h(Modal, {
25
- title: props.label || 'Help',
26
- onClose: function() { setOpen(false); },
27
- large: true
24
+ isOpen && h('div', {
25
+ style: { position: 'fixed', inset: 0, background: 'var(--bg-modal, rgba(0,0,0,0.7))', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 200, animation: 'fadeIn 150ms ease' },
26
+ onClick: function(e) { if (e.target === e.currentTarget) setOpen(false); }
28
27
  },
29
- h('div', { style: { fontSize: 14, lineHeight: 1.7, color: 'var(--text-secondary, #9ca3af)', padding: '4px 0' } },
30
- props.children
28
+ h('div', { className: 'modal', style: { zIndex: 201 } },
29
+ h('div', { className: 'modal-header' },
30
+ h('h2', null, props.label || 'Help'),
31
+ h('button', { className: 'btn btn-ghost btn-icon', onClick: function() { setOpen(false); } }, I.x())
32
+ ),
33
+ h('div', { className: 'modal-body' },
34
+ h('div', { style: { fontSize: 14, lineHeight: 1.7, color: 'var(--text-secondary, #9ca3af)', padding: '4px 0' } },
35
+ props.children
36
+ )
37
+ )
31
38
  )
32
39
  )
33
40
  );
@@ -248,7 +248,10 @@ function McpServersSection() {
248
248
  h('div', { style: { display: 'flex', flexDirection: 'column', gap: 14 } },
249
249
  // Name
250
250
  h('div', { className: 'form-group' },
251
- h('label', { className: 'form-label' }, 'Server Name *'),
251
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Server Name *', h(HelpButton, { label: 'Server Name' },
252
+ h('p', null, 'A friendly name so you can identify this server in the list. Use something descriptive like "GitHub Tools" or "Company Database".'),
253
+ h('p', null, 'This name is only for your reference — it doesn\'t affect how the server works.')
254
+ )),
252
255
  h('input', { className: 'input', placeholder: 'e.g., GitHub MCP, Filesystem, Database', value: form.name,
253
256
  onChange: function(e) { setForm(Object.assign({}, form, { name: e.target.value })); } })
254
257
  ),
@@ -260,7 +263,16 @@ function McpServersSection() {
260
263
  ),
261
264
  // Type selector
262
265
  h('div', { className: 'form-group' },
263
- h('label', { className: 'form-label' }, 'Connection Type *'),
266
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Connection Type *', h(HelpButton, { label: 'Connection Type' },
267
+ h('p', null, 'How your agents communicate with this MCP server. If you\'re not sure, choose ', h('strong', null, 'Local Process'), ' — it\'s the most common and works out of the box.'),
268
+ h('ul', { style: { paddingLeft: 20, margin: '8px 0' } },
269
+ h('li', null, h('strong', null, 'Local Process'), ' — The server runs as a program on the same machine. Best for most use cases. Just provide the command to start it (like "npx" or "docker").'),
270
+ h('li', null, h('strong', null, 'SSE'), ' — Connects to an MCP server hosted elsewhere (another machine or cloud). Uses a live streaming connection.'),
271
+ h('li', null, h('strong', null, 'HTTP'), ' — Connects to a remote MCP server using simple web requests. Used by some cloud-hosted MCP services.')
272
+ ),
273
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
274
+ h('strong', null, 'Not sure? '), 'Use a Quick Start Template below — it pre-fills everything for you.')
275
+ )),
264
276
  h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8 } },
265
277
  ['stdio', 'sse', 'http'].map(function(t) {
266
278
  var selected = form.type === t;
@@ -275,7 +287,7 @@ function McpServersSection() {
275
287
  },
276
288
  h('div', { style: { fontWeight: 600, fontSize: 12 } }, t === 'stdio' ? 'Local Process' : t === 'sse' ? 'SSE' : 'HTTP'),
277
289
  h('div', { style: { fontSize: 10, color: 'var(--text-muted)', marginTop: 2 } },
278
- t === 'stdio' ? 'stdin/stdout' : t === 'sse' ? 'Server-Sent Events' : 'Streamable HTTP')
290
+ t === 'stdio' ? 'Runs on this machine' : t === 'sse' ? 'Remote with live stream' : 'Remote web requests')
279
291
  );
280
292
  })
281
293
  )
@@ -283,56 +295,110 @@ function McpServersSection() {
283
295
  // stdio fields
284
296
  form.type === 'stdio' && h(Fragment, null,
285
297
  h('div', { className: 'form-group' },
286
- h('label', { className: 'form-label' }, 'Command *'),
298
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Command *', h(HelpButton, { label: 'Command' },
299
+ h('p', null, 'The program to run on your server. This is like typing a command in the terminal. Common examples:'),
300
+ h('ul', { style: { paddingLeft: 20, margin: '8px 0' } },
301
+ h('li', null, h('strong', null, 'npx'), ' — Runs Node.js packages without installing them first. Most MCP servers use this.'),
302
+ h('li', null, h('strong', null, 'node'), ' — Runs a JavaScript file directly.'),
303
+ h('li', null, h('strong', null, 'python'), ' or ', h('strong', null, 'python3'), ' — Runs Python-based MCP servers.'),
304
+ h('li', null, h('strong', null, 'docker'), ' — Runs the server inside a Docker container (advanced).')
305
+ ),
306
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
307
+ h('strong', null, 'Tip: '), 'If you chose a Quick Start Template, this is already filled in. Most templates use "npx" which requires Node.js to be installed on your machine.')
308
+ )),
287
309
  h('input', { className: 'input', placeholder: 'npx, node, python, docker...', value: form.command,
288
310
  onChange: function(e) { setForm(Object.assign({}, form, { command: e.target.value })); } }),
289
- h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'The executable to run. Must be installed on this machine.')
311
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'The program to start the MCP server. Must be installed on this machine.')
290
312
  ),
291
313
  h('div', { className: 'form-group' },
292
- h('label', { className: 'form-label' }, 'Arguments'),
293
- h('input', { className: 'input', placeholder: '@modelcontextprotocol/server-filesystem /home/user/docs', value: form.args,
314
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Arguments', h(HelpButton, { label: 'Arguments' },
315
+ h('p', null, 'Extra instructions passed to the command. Think of it like telling the program what to do. For example:'),
316
+ h('ul', { style: { paddingLeft: 20, margin: '8px 0' } },
317
+ h('li', null, h('strong', null, '-y @modelcontextprotocol/server-filesystem /home'), ' — Tells npx to run the filesystem MCP server and give it access to /home'),
318
+ h('li', null, h('strong', null, '-y @modelcontextprotocol/server-github'), ' — Tells npx to run the GitHub MCP server')
319
+ ),
320
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
321
+ h('strong', null, 'Tip: '), 'The "-y" flag tells npx to automatically install the package if needed, without asking. Always include it for npx commands.')
322
+ )),
323
+ h('input', { className: 'input', placeholder: '-y @modelcontextprotocol/server-filesystem /home/user/docs', value: form.args,
294
324
  onChange: function(e) { setForm(Object.assign({}, form, { args: e.target.value })); } }),
295
- h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Space-separated arguments passed to the command.')
325
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Additional options passed to the command. Separate multiple values with spaces.')
296
326
  ),
297
327
  h('div', { className: 'form-group' },
298
- h('label', { className: 'form-label' }, 'Environment Variables'),
328
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Environment Variables', h(HelpButton, { label: 'Environment Variables' },
329
+ h('p', null, 'Some MCP servers need passwords, API keys, or settings to work. Environment variables are a secure way to pass this information.'),
330
+ h('p', { style: { marginTop: 8 } }, 'Format: a JSON object where each key is the variable name and each value is the secret. For example:'),
331
+ h('pre', { style: { background: 'var(--bg-secondary)', padding: 10, borderRadius: 8, fontSize: 12, marginTop: 8 } },
332
+ '{\n "GITHUB_TOKEN": "ghp_abc123...",\n "DATABASE_URL": "postgres://user:pass@host/db"\n}'
333
+ ),
334
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
335
+ h('strong', null, 'Security: '), 'All values are encrypted before being stored. They\'re only decrypted when the server starts.')
336
+ )),
299
337
  h('textarea', { className: 'input', rows: 3, placeholder: '{\n "GITHUB_TOKEN": "ghp_...",\n "DATABASE_URL": "postgres://..."\n}', value: form.env,
300
338
  style: { fontFamily: 'var(--font-mono, monospace)', fontSize: 12 },
301
339
  onChange: function(e) { setForm(Object.assign({}, form, { env: e.target.value })); } }),
302
- h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'JSON object of environment variables. Secrets are encrypted at rest.')
340
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'API keys, tokens, and passwords needed by this server. Stored encrypted.')
303
341
  )
304
342
  ),
305
343
  // HTTP/SSE fields
306
344
  form.type !== 'stdio' && h(Fragment, null,
307
345
  h('div', { className: 'form-group' },
308
- h('label', { className: 'form-label' }, 'Server URL *'),
346
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Server URL *', h(HelpButton, { label: 'Server URL' },
347
+ h('p', null, 'The web address of the remote MCP server. This is provided by whoever hosts the server.'),
348
+ h('p', { style: { marginTop: 8 } }, 'It usually looks like:'),
349
+ h('ul', { style: { paddingLeft: 20, margin: '4px 0' } },
350
+ h('li', null, h('strong', null, 'SSE: '), 'https://mcp.example.com/sse'),
351
+ h('li', null, h('strong', null, 'HTTP: '), 'https://mcp.example.com/mcp')
352
+ ),
353
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
354
+ h('strong', null, 'Tip: '), 'Check the MCP server\'s documentation for the exact URL to use. If it\'s hosted on your local network, use the internal IP (e.g., http://192.168.1.100:3000/mcp).')
355
+ )),
309
356
  h('input', { className: 'input', placeholder: form.type === 'sse' ? 'https://mcp.example.com/sse' : 'https://mcp.example.com/mcp', value: form.url,
310
- onChange: function(e) { setForm(Object.assign({}, form, { url: e.target.value })); } })
357
+ onChange: function(e) { setForm(Object.assign({}, form, { url: e.target.value })); } }),
358
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'The web address where the MCP server is running.')
311
359
  ),
312
360
  h('div', { className: 'form-group' },
313
- h('label', { className: 'form-label' }, 'API Key / Bearer Token'),
314
- h('input', { className: 'input', type: 'password', placeholder: 'Optional for authenticated endpoints', value: form.apiKey,
315
- onChange: function(e) { setForm(Object.assign({}, form, { apiKey: e.target.value })); } })
361
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'API Key / Bearer Token', h(HelpButton, { label: 'API Key' },
362
+ h('p', null, 'If the remote server requires authentication, paste the API key or token here. This is like a password that proves you\'re allowed to use the server.'),
363
+ h('p', { style: { marginTop: 8 } }, 'You usually get this from the MCP server provider\'s dashboard or settings page. If the server is open/public, leave this empty.')
364
+ )),
365
+ h('input', { className: 'input', type: 'password', placeholder: 'Optional — only if the server requires authentication', value: form.apiKey,
366
+ onChange: function(e) { setForm(Object.assign({}, form, { apiKey: e.target.value })); } }),
367
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Only needed if the server requires authentication. Leave empty for open servers.')
316
368
  ),
317
369
  h('div', { className: 'form-group' },
318
- h('label', { className: 'form-label' }, 'Custom Headers'),
370
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Custom Headers', h(HelpButton, { label: 'Custom Headers' },
371
+ h('p', null, 'Advanced setting. Some servers need extra information sent with every request (like an organization ID or custom authentication format).'),
372
+ h('p', { style: { marginTop: 8 } }, 'Most users can leave this empty. If you need it, your MCP server provider will tell you what to put here.'),
373
+ h('div', { style: { marginTop: 8, padding: 10, background: 'var(--bg-secondary)', borderRadius: 8, fontSize: 12 } },
374
+ h('strong', null, 'Tip: '), 'This is rarely needed. If you\'re not sure, skip it.')
375
+ )),
319
376
  h('textarea', { className: 'input', rows: 2, placeholder: '{\n "X-Custom-Header": "value"\n}', value: form.headers,
320
377
  style: { fontFamily: 'var(--font-mono, monospace)', fontSize: 12 },
321
- onChange: function(e) { setForm(Object.assign({}, form, { headers: e.target.value })); } })
378
+ onChange: function(e) { setForm(Object.assign({}, form, { headers: e.target.value })); } }),
379
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Advanced. Usually not needed — leave empty unless told otherwise.')
322
380
  )
323
381
  ),
324
382
  // Common settings
325
383
  h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 } },
326
384
  h('div', { className: 'form-group' },
327
- h('label', { className: 'form-label' }, 'Connection Timeout (s)'),
385
+ h('label', { className: 'form-label', style: { display: 'flex', alignItems: 'center' } }, 'Connection Timeout', h(HelpButton, { label: 'Timeout' },
386
+ h('p', null, 'How long to wait (in seconds) for the server to respond before giving up. The default of 30 seconds works for most servers.'),
387
+ h('p', { style: { marginTop: 8 } }, 'Increase this if the server is slow to start (e.g., Docker containers) or on a slow network.')
388
+ )),
328
389
  h('input', { className: 'input', type: 'number', min: 5, max: 300, value: form.timeout,
329
- onChange: function(e) { setForm(Object.assign({}, form, { timeout: e.target.value })); } })
390
+ onChange: function(e) { setForm(Object.assign({}, form, { timeout: e.target.value })); } }),
391
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Seconds to wait. 30 is usually fine.')
330
392
  ),
331
393
  h('div', { className: 'form-group' },
332
394
  h('label', { style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, marginTop: 24 } },
333
395
  h('input', { type: 'checkbox', checked: form.autoRestart,
334
396
  onChange: function(e) { setForm(Object.assign({}, form, { autoRestart: e.target.checked })); } }),
335
- 'Auto-restart on failure'
397
+ 'Auto-restart on failure',
398
+ h(HelpButton, { label: 'Auto-restart' },
399
+ h('p', null, 'If the MCP server crashes or stops unexpectedly, we\'ll automatically restart it so your agents don\'t lose access to its tools.'),
400
+ h('p', { style: { marginTop: 8 } }, 'Keep this on unless you have a reason to disable it.')
401
+ )
336
402
  )
337
403
  )
338
404
  ),