fa-mcp-sdk 0.4.17 → 0.4.19

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.
@@ -1,6 +1,32 @@
1
1
  const API_BASE = '/agent-tester';
2
2
  const trim = (s) => String(s || '').trim();
3
3
 
4
+ const LLM_LS_KEY = 'mcpAgentLlmSettings';
5
+ const LLM_PRESET_MODELS = [
6
+ 'gpt-5.4',
7
+ 'gpt-5.4-mini',
8
+ 'gpt-5.4-nano',
9
+ 'gpt-5.3-codex',
10
+ 'gpt-5.2',
11
+ 'gpt-5.1',
12
+ 'gpt-5-nano',
13
+ 'gpt-5-mini',
14
+ 'gpt-4.1',
15
+ 'gpt-4.1-mini',
16
+ 'gpt-4.1-nano',
17
+ 'gpt-4o',
18
+ 'gpt-4o-mini',
19
+ ];
20
+ const LLM_DEFAULTS = {
21
+ baseURL: '',
22
+ apiKey: '',
23
+ model: 'gpt-5.4-nano',
24
+ temperature: 0.2,
25
+ maxTokens: 2048,
26
+ maxTurns: 10,
27
+ toolResultLimitChars: 20000,
28
+ };
29
+
4
30
  /**
5
31
  * Wrapper around fetch that always includes credentials (session cookie).
6
32
  */
@@ -348,16 +374,26 @@ class McpAgentTester {
348
374
  this.headersSection = document.getElementById('headersSection');
349
375
  this.dynamicHeaders = document.getElementById('dynamicHeaders');
350
376
 
351
- this.modelSelect = document.getElementById('modelSelect');
352
-
353
- this.customModelSettings = document.getElementById('customModelSettings');
354
- this.customBaseUrl = document.getElementById('customBaseUrl');
355
- this.customApiKey = document.getElementById('customApiKey');
356
- this.customModelName = document.getElementById('customModelName');
357
- this.modelTemperature = document.getElementById('modelTemperature');
358
- this.modelMaxTokens = document.getElementById('modelMaxTokens');
359
- this.modelMaxTurns = document.getElementById('modelMaxTurns');
360
- this.toolResultLimitChars = document.getElementById('toolResultLimitChars');
377
+ // LLM settings — collapsed view + modal
378
+ this.modelDisplay = document.getElementById('modelDisplay');
379
+ this.llmSettingsBtn = document.getElementById('llmSettingsBtn');
380
+ this.apiKeyWarning = document.getElementById('apiKeyWarning');
381
+ this.llmModal = document.getElementById('llmModal');
382
+ this.llmModalClose = document.getElementById('llmModalClose');
383
+ this.llmModalCancel = document.getElementById('llmModalCancel');
384
+ this.llmModalSave = document.getElementById('llmModalSave');
385
+ this.llmApiKeyToggle = document.getElementById('llmApiKeyToggle');
386
+ this.llmBaseUrl = document.getElementById('llmBaseUrl');
387
+ this.llmApiKey = document.getElementById('llmApiKey');
388
+ this.llmModelName = document.getElementById('llmModelName');
389
+ this.llmModelDropdownToggle = document.getElementById('llmModelDropdownToggle');
390
+ this.llmModelDropdownList = document.getElementById('llmModelDropdownList');
391
+ this.llmTemperature = document.getElementById('llmTemperature');
392
+ this.llmMaxTokens = document.getElementById('llmMaxTokens');
393
+ this.llmMaxTurns = document.getElementById('llmMaxTurns');
394
+ this.llmLimitChars = document.getElementById('llmLimitChars');
395
+
396
+ this.llmSettings = { ...LLM_DEFAULTS };
361
397
 
362
398
  this.systemPromptTextarea = document.getElementById('systemPrompt');
363
399
  this.customPromptTextarea = document.getElementById('customPrompt');
@@ -404,20 +440,25 @@ class McpAgentTester {
404
440
  this.serverUrlDropdown.addEventListener('click', (e) => this.toggleUrlDropdown(e));
405
441
  document.addEventListener('click', (e) => this.handleClickOutside(e));
406
442
 
407
- this.modelSelect.addEventListener('change', () => {
408
- this.handleModelSelectChange();
409
- this.saveFormValuesToStorage();
410
- });
411
443
  this.systemPromptTextarea.addEventListener('input', () => this.saveFormValuesToStorage());
412
444
  this.customPromptTextarea.addEventListener('input', () => this.saveFormValuesToStorage());
413
445
 
414
- this.customBaseUrl.addEventListener('input', () => this.saveFormValuesToStorage());
415
- this.customApiKey.addEventListener('input', () => this.saveFormValuesToStorage());
416
- this.customModelName.addEventListener('input', () => this.saveFormValuesToStorage());
417
- this.modelTemperature.addEventListener('input', () => this.saveFormValuesToStorage());
418
- this.modelMaxTokens.addEventListener('input', () => this.saveFormValuesToStorage());
419
- this.modelMaxTurns.addEventListener('input', () => this.saveFormValuesToStorage());
420
- this.toolResultLimitChars.addEventListener('input', () => this.saveFormValuesToStorage());
446
+ // LLM settings modal
447
+ this.llmSettingsBtn.addEventListener('click', () => this.openLlmModal());
448
+ this.llmModalClose.addEventListener('click', () => this.closeLlmModal());
449
+ this.llmModalCancel.addEventListener('click', () => this.closeLlmModal());
450
+ this.llmModalSave.addEventListener('click', () => this.saveLlmModal());
451
+ this.llmApiKeyToggle.addEventListener('click', () => this.toggleApiKeyVisibility());
452
+ this.llmModelDropdownToggle.addEventListener('click', (e) => this.toggleLlmModelDropdown(e));
453
+ this.renderLlmModelDropdown();
454
+ this.llmModal.addEventListener('click', (e) => {
455
+ if (e.target === this.llmModal) { this.closeLlmModal(); }
456
+ });
457
+ document.addEventListener('keydown', (e) => {
458
+ if (e.key === 'Escape' && this.llmModal.style.display === 'flex') {
459
+ this.closeLlmModal();
460
+ }
461
+ });
421
462
 
422
463
  document.querySelectorAll('.btn-enlarge').forEach(btn => {
423
464
  btn.addEventListener('click', () => this.openPromptModal(btn.dataset.target));
@@ -518,6 +559,7 @@ class McpAgentTester {
518
559
  this.handleServerUrlChange();
519
560
  this.renderSavedUrls();
520
561
  await this.loadDefaultConfig();
562
+ this.initLlmSettings();
521
563
  await this.loadCurrentServer();
522
564
  this.currentSystemPrompt = this.systemPromptTextarea.value;
523
565
 
@@ -606,6 +648,7 @@ class McpAgentTester {
606
648
  this.defaultMcpUrl = config.defaultMcpUrl || null;
607
649
  this.authEnabled = !!config.authEnabled;
608
650
  this.configHttpHeaders = config.httpHeaders || {};
651
+ this.llmDefaults = config.llmDefaults || {};
609
652
  if (config.defaultMcpUrl) {
610
653
  const serverUrlInput = document.getElementById('serverUrl');
611
654
  if (!this.mcpConfig.url && !serverUrlInput.value) {
@@ -1176,7 +1219,7 @@ class McpAgentTester {
1176
1219
  const message = this.messageInput.value.trim();
1177
1220
  if (!message) {return;}
1178
1221
 
1179
- if (!this.validateCustomModelSettings()) {
1222
+ if (!this.validateLlmSettings()) {
1180
1223
  return;
1181
1224
  }
1182
1225
 
@@ -1379,63 +1422,209 @@ class McpAgentTester {
1379
1422
  });
1380
1423
  }
1381
1424
 
1382
- handleModelSelectChange () {
1383
- const isOther = this.modelSelect.value === 'other';
1384
- this.customModelSettings.style.display = isOther ? 'block' : 'none';
1425
+ initLlmSettings () {
1426
+ let stored = {};
1427
+ try {
1428
+ stored = JSON.parse(localStorage.getItem(LLM_LS_KEY) || '{}');
1429
+ } catch { stored = {}; }
1430
+
1431
+ const merged = { ...LLM_DEFAULTS, ...stored };
1432
+ const cfg = this.llmDefaults || {};
1433
+ let touched = false;
1434
+
1435
+ if (!merged.baseURL && cfg.baseURL) { merged.baseURL = cfg.baseURL; touched = true; }
1436
+ if (!merged.apiKey && cfg.apiKey) { merged.apiKey = cfg.apiKey; touched = true; }
1437
+
1438
+ this.llmSettings = merged;
1439
+
1440
+ if (touched) { this.saveLlmSettings(); }
1441
+
1442
+ this.migrateLegacyLlmSettings();
1443
+ this.renderModelDisplay();
1385
1444
  }
1386
1445
 
1387
- validateCustomModelSettings () {
1388
- if (this.modelSelect.value !== 'other') {
1389
- return true;
1446
+ migrateLegacyLlmSettings () {
1447
+ try {
1448
+ const legacy = JSON.parse(localStorage.getItem('mcpAgentFormValues') || '{}');
1449
+ let dirty = false;
1450
+ const numericFields = new Set(['temperature', 'maxTokens', 'maxTurns', 'toolResultLimitChars']);
1451
+ const map = {
1452
+ customBaseUrl: 'baseURL',
1453
+ customApiKey: 'apiKey',
1454
+ customModelName: 'model',
1455
+ modelTemperature: 'temperature',
1456
+ modelMaxTokens: 'maxTokens',
1457
+ modelMaxTurns: 'maxTurns',
1458
+ toolResultLimitChars: 'toolResultLimitChars',
1459
+ };
1460
+ for (const [from, to] of Object.entries(map)) {
1461
+ const raw = legacy[from];
1462
+ if (raw == null || raw === '') { continue; }
1463
+ const current = this.llmSettings[to];
1464
+ const isEmpty = current == null || current === '' || current === LLM_DEFAULTS[to];
1465
+ if (!isEmpty) { continue; }
1466
+ const v = numericFields.has(to) ? Number(raw) : raw;
1467
+ if (numericFields.has(to) && Number.isNaN(v)) { continue; }
1468
+ this.llmSettings[to] = v;
1469
+ dirty = true;
1470
+ }
1471
+ // Legacy preset model (not 'other'): reuse if current model is still a default
1472
+ if (legacy.model && legacy.model !== 'other' && (this.llmSettings.model === LLM_DEFAULTS.model || !this.llmSettings.model)) {
1473
+ this.llmSettings.model = legacy.model;
1474
+ dirty = true;
1475
+ }
1476
+ if (dirty) { this.saveLlmSettings(); this.renderModelDisplay(); }
1477
+ } catch { /* ignore */ }
1478
+ }
1479
+
1480
+ saveLlmSettings () {
1481
+ try {
1482
+ localStorage.setItem(LLM_LS_KEY, JSON.stringify(this.llmSettings));
1483
+ } catch (e) {
1484
+ console.error('Failed to save LLM settings:', e);
1390
1485
  }
1486
+ }
1391
1487
 
1392
- const baseURL = trim(this.customBaseUrl.value);
1393
- const apiKey = trim(this.customApiKey.value);
1394
- const modelName = trim(this.customModelName.value);
1395
- const temperature = this.modelTemperature.value;
1396
- const maxTokens = this.modelMaxTokens.value;
1488
+ renderModelDisplay () {
1489
+ const name = trim(this.llmSettings.model) || '—';
1490
+ this.modelDisplay.textContent = name;
1491
+ this.apiKeyWarning.style.display = this.llmSettings.apiKey ? 'none' : 'block';
1492
+ }
1397
1493
 
1398
- const missingFields = [];
1399
- if (!baseURL) {missingFields.push('Base URL');}
1400
- if (!apiKey) {missingFields.push('API Key');}
1401
- if (!modelName) {missingFields.push('Model Name');}
1402
- if (!temperature) {missingFields.push('Temperature');}
1403
- if (!maxTokens) {missingFields.push('Max Tokens');}
1494
+ openLlmModal () {
1495
+ const s = this.llmSettings;
1496
+ this.llmBaseUrl.value = s.baseURL || '';
1497
+ this.llmApiKey.value = s.apiKey || '';
1498
+ this.llmModelName.value = s.model || '';
1499
+ this.llmTemperature.value = s.temperature ?? LLM_DEFAULTS.temperature;
1500
+ this.llmMaxTokens.value = s.maxTokens ?? LLM_DEFAULTS.maxTokens;
1501
+ this.llmMaxTurns.value = s.maxTurns ?? LLM_DEFAULTS.maxTurns;
1502
+ this.llmLimitChars.value = s.toolResultLimitChars ?? LLM_DEFAULTS.toolResultLimitChars;
1404
1503
 
1405
- if (missingFields.length > 0) {
1406
- this.showToast(`Missing required fields: ${missingFields.join(', ')}`, 'error');
1407
- return false;
1504
+ // Reset API key visibility to hidden on open
1505
+ this.llmApiKey.type = 'password';
1506
+ const icon = this.llmApiKeyToggle.querySelector('.material-icons-round');
1507
+ if (icon) { icon.textContent = 'visibility'; }
1508
+
1509
+ this.llmModal.style.display = 'flex';
1510
+ }
1511
+
1512
+ closeLlmModal () {
1513
+ this.closeLlmModelDropdown();
1514
+ this.llmModal.style.display = 'none';
1515
+ }
1516
+
1517
+ saveLlmModal () {
1518
+ const baseURL = trim(this.llmBaseUrl.value);
1519
+ const apiKey = trim(this.llmApiKey.value);
1520
+ const model = trim(this.llmModelName.value);
1521
+ const temperature = parseFloat(this.llmTemperature.value);
1522
+ const maxTokens = parseInt(this.llmMaxTokens.value, 10);
1523
+ const maxTurns = parseInt(this.llmMaxTurns.value, 10);
1524
+ const toolResultLimitChars = parseInt(this.llmLimitChars.value, 10);
1525
+
1526
+ const missing = [];
1527
+ if (!model) { missing.push('Model Name'); }
1528
+ // baseURL is optional (OpenAI default) — empty means use provider default
1529
+ // apiKey intentionally not required here — its absence triggers the red warning instead
1530
+ if (Number.isNaN(temperature)) { missing.push('Temperature'); }
1531
+ if (!maxTokens) { missing.push('Max Tokens'); }
1532
+ if (!maxTurns) { missing.push('Max Turns'); }
1533
+ if (!toolResultLimitChars) { missing.push('Limit (chars)'); }
1534
+
1535
+ if (missing.length) {
1536
+ this.showToast(`Missing required fields: ${missing.join(', ')}`, 'error');
1537
+ return;
1408
1538
  }
1409
1539
 
1410
- return true;
1540
+ this.llmSettings = { baseURL, apiKey, model, temperature, maxTokens, maxTurns, toolResultLimitChars };
1541
+ this.saveLlmSettings();
1542
+ this.renderModelDisplay();
1543
+ this.closeLlmModal();
1544
+ this.showToast('LLM settings saved', 'success');
1411
1545
  }
1412
1546
 
1413
- getModelConfig () {
1414
- const isOther = this.modelSelect.value === 'other';
1415
- const t = parseFloat(this.modelTemperature.value);
1416
- const temperature = Number.isNaN(t) ? 0.1 : t;
1417
- const maxTokens = parseInt(this.modelMaxTokens.value, 10) || 2048;
1418
- const maxTurns = parseInt(this.modelMaxTurns.value, 10) || 10;
1419
- const toolResultLimitChars = parseInt(this.toolResultLimitChars.value, 10) || 20000;
1420
-
1421
- if (isOther) {
1422
- return {
1423
- baseURL: trim(this.customBaseUrl.value),
1424
- apiKey: trim(this.customApiKey.value),
1425
- model: trim(this.customModelName.value),
1426
- temperature: temperature,
1427
- maxTokens: maxTokens,
1428
- maxTurns: maxTurns,
1429
- toolResultLimitChars: toolResultLimitChars,
1547
+ toggleApiKeyVisibility () {
1548
+ const icon = this.llmApiKeyToggle.querySelector('.material-icons-round');
1549
+ if (this.llmApiKey.type === 'password') {
1550
+ this.llmApiKey.type = 'text';
1551
+ if (icon) { icon.textContent = 'visibility_off'; }
1552
+ } else {
1553
+ this.llmApiKey.type = 'password';
1554
+ if (icon) { icon.textContent = 'visibility'; }
1555
+ }
1556
+ }
1557
+
1558
+ renderLlmModelDropdown () {
1559
+ this.llmModelDropdownList.innerHTML = '';
1560
+ LLM_PRESET_MODELS.forEach((name) => {
1561
+ const item = document.createElement('div');
1562
+ item.className = 'dropdown-item';
1563
+ item.setAttribute('data-testid', `at-llm-model-option-${name}`);
1564
+ item.textContent = name;
1565
+ item.addEventListener('click', (e) => {
1566
+ e.stopPropagation();
1567
+ this.llmModelName.value = name;
1568
+ this.closeLlmModelDropdown();
1569
+ });
1570
+ this.llmModelDropdownList.appendChild(item);
1571
+ });
1572
+ }
1573
+
1574
+ toggleLlmModelDropdown (e) {
1575
+ e.preventDefault();
1576
+ e.stopPropagation();
1577
+ const visible = this.llmModelDropdownList.style.display !== 'none';
1578
+ if (visible) {
1579
+ this.closeLlmModelDropdown();
1580
+ } else {
1581
+ this.openLlmModelDropdown();
1582
+ }
1583
+ }
1584
+
1585
+ openLlmModelDropdown () {
1586
+ this.llmModelDropdownList.style.display = 'block';
1587
+ this.llmModelDropdownToggle.classList.add('active');
1588
+ // Close on outside click (one-shot)
1589
+ setTimeout(() => {
1590
+ const onDocClick = (ev) => {
1591
+ if (!this.llmModelDropdownList.contains(ev.target) && ev.target !== this.llmModelDropdownToggle) {
1592
+ this.closeLlmModelDropdown();
1593
+ document.removeEventListener('click', onDocClick);
1594
+ }
1430
1595
  };
1596
+ document.addEventListener('click', onDocClick);
1597
+ }, 0);
1598
+ }
1599
+
1600
+ closeLlmModelDropdown () {
1601
+ this.llmModelDropdownList.style.display = 'none';
1602
+ this.llmModelDropdownToggle.classList.remove('active');
1603
+ }
1604
+
1605
+ validateLlmSettings () {
1606
+ const s = this.llmSettings;
1607
+ const missing = [];
1608
+ // baseURL is optional — empty means use provider default (OpenAI)
1609
+ if (!s.apiKey) { missing.push('API Key'); }
1610
+ if (!s.model) { missing.push('Model Name'); }
1611
+ if (missing.length) {
1612
+ this.showToast(`Cannot send message — missing: ${missing.join(', ')}. Open LLM Settings.`, 'error');
1613
+ return false;
1431
1614
  }
1615
+ return true;
1616
+ }
1432
1617
 
1618
+ getModelConfig () {
1619
+ const s = this.llmSettings;
1433
1620
  return {
1434
- model: this.modelSelect.value,
1435
- temperature: temperature,
1436
- maxTokens: maxTokens,
1437
- maxTurns: maxTurns,
1438
- toolResultLimitChars: toolResultLimitChars,
1621
+ baseURL: s.baseURL,
1622
+ apiKey: s.apiKey,
1623
+ model: s.model,
1624
+ temperature: s.temperature,
1625
+ maxTokens: s.maxTokens,
1626
+ maxTurns: s.maxTurns,
1627
+ toolResultLimitChars: s.toolResultLimitChars,
1439
1628
  };
1440
1629
  }
1441
1630
 
@@ -1471,16 +1660,8 @@ class McpAgentTester {
1471
1660
  const formData = {
1472
1661
  serverUrl: this.serverUrlInput.value,
1473
1662
  transport: this.transportSelect.value,
1474
- model: this.modelSelect.value,
1475
1663
  agentPrompt: trim(this.systemPromptTextarea.value),
1476
1664
  customPrompt: trim(this.customPromptTextarea.value),
1477
- customBaseUrl: trim(this.customBaseUrl.value),
1478
- customApiKey: trim(this.customApiKey.value),
1479
- customModelName: trim(this.customModelName.value),
1480
- modelTemperature: this.modelTemperature.value,
1481
- modelMaxTokens: this.modelMaxTokens.value,
1482
- modelMaxTurns: this.modelMaxTurns.value,
1483
- toolResultLimitChars: this.toolResultLimitChars.value,
1484
1665
  };
1485
1666
  localStorage.setItem('mcpAgentFormValues', JSON.stringify(formData));
1486
1667
  }
@@ -1509,17 +1690,8 @@ class McpAgentTester {
1509
1690
  const formData = JSON.parse(stored);
1510
1691
  if (formData.serverUrl) {this.serverUrlInput.value = formData.serverUrl;}
1511
1692
  if (formData.transport) {this.transportSelect.value = formData.transport;}
1512
- if (formData.model) {this.modelSelect.value = formData.model;}
1513
1693
  if (formData.agentPrompt) {this.systemPromptTextarea.value = trim(formData.agentPrompt);}
1514
1694
  if (formData.customPrompt) {this.customPromptTextarea.value = trim(formData.customPrompt);}
1515
- if (formData.customBaseUrl) {this.customBaseUrl.value = formData.customBaseUrl;}
1516
- if (formData.customApiKey) {this.customApiKey.value = formData.customApiKey;}
1517
- if (formData.customModelName) {this.customModelName.value = formData.customModelName;}
1518
- if (formData.modelTemperature) {this.modelTemperature.value = formData.modelTemperature;}
1519
- if (formData.modelMaxTokens) {this.modelMaxTokens.value = formData.modelMaxTokens;}
1520
- if (formData.modelMaxTurns) {this.modelMaxTurns.value = formData.modelMaxTurns;}
1521
- if (formData.toolResultLimitChars) {this.toolResultLimitChars.value = formData.toolResultLimitChars;}
1522
- this.handleModelSelectChange();
1523
1695
  }
1524
1696
  } catch (error) {
1525
1697
  console.error('Error loading form values from storage:', error);
@@ -1489,6 +1489,140 @@ body {
1489
1489
  opacity: 0.9;
1490
1490
  }
1491
1491
 
1492
+ /* ============================================
1493
+ Model Display (collapsed LLM block)
1494
+ ============================================ */
1495
+
1496
+ .model-display-row {
1497
+ display: flex;
1498
+ align-items: center;
1499
+ gap: 8px;
1500
+ }
1501
+
1502
+ .model-display {
1503
+ flex: 1;
1504
+ padding: 8px 12px;
1505
+ background: var(--bg-input);
1506
+ border: 1px solid var(--border-input);
1507
+ border-radius: var(--radius);
1508
+ font-family: 'Fira Code', monospace;
1509
+ font-size: 0.85rem;
1510
+ color: var(--text);
1511
+ user-select: text;
1512
+ overflow: hidden;
1513
+ text-overflow: ellipsis;
1514
+ white-space: nowrap;
1515
+ }
1516
+
1517
+ .api-key-warning {
1518
+ margin-top: 6px;
1519
+ color: #d93025;
1520
+ font-size: 0.75rem;
1521
+ font-weight: 500;
1522
+ }
1523
+
1524
+ [data-theme="dark"] .api-key-warning {
1525
+ color: #f28b82;
1526
+ }
1527
+
1528
+ /* ============================================
1529
+ LLM Settings Modal
1530
+ ============================================ */
1531
+
1532
+ .llm-modal-overlay {
1533
+ position: fixed;
1534
+ inset: 0;
1535
+ background: rgba(0, 0, 0, 0.5);
1536
+ backdrop-filter: blur(4px);
1537
+ z-index: 1000;
1538
+ display: flex;
1539
+ align-items: center;
1540
+ justify-content: center;
1541
+ padding: 24px;
1542
+ }
1543
+
1544
+ .llm-modal {
1545
+ background: var(--bg-sidebar);
1546
+ border: 1px solid var(--border);
1547
+ border-radius: var(--radius-lg);
1548
+ width: 100%;
1549
+ max-width: 520px;
1550
+ max-height: 95vh;
1551
+ display: flex;
1552
+ flex-direction: column;
1553
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
1554
+ }
1555
+
1556
+ /* Compact dropdown items inside LLM modal so all preset models fit without scroll */
1557
+ .llm-modal .dropdown-list {
1558
+ max-height: 420px;
1559
+ }
1560
+
1561
+ .llm-modal .dropdown-item {
1562
+ padding: 4px 12px;
1563
+ font-size: 0.8rem;
1564
+ }
1565
+
1566
+ .llm-modal-header {
1567
+ display: flex;
1568
+ align-items: center;
1569
+ justify-content: space-between;
1570
+ padding: 16px 20px;
1571
+ border-bottom: 1px solid var(--border);
1572
+ }
1573
+
1574
+ .llm-modal-header h3 {
1575
+ font-size: 0.95rem;
1576
+ font-weight: 600;
1577
+ color: var(--text);
1578
+ margin: 0;
1579
+ }
1580
+
1581
+ .llm-modal-body {
1582
+ padding: 16px 20px;
1583
+ overflow: visible;
1584
+ display: flex;
1585
+ flex-direction: column;
1586
+ gap: 12px;
1587
+ }
1588
+
1589
+ .llm-modal-body .form-row {
1590
+ display: flex;
1591
+ gap: 12px;
1592
+ }
1593
+
1594
+ .llm-modal-body .form-row .form-group {
1595
+ flex: 1;
1596
+ }
1597
+
1598
+ .llm-modal-body .input-with-toggle {
1599
+ display: flex;
1600
+ gap: 4px;
1601
+ align-items: stretch;
1602
+ }
1603
+
1604
+ .llm-modal-body .input-with-toggle input {
1605
+ flex: 1;
1606
+ }
1607
+
1608
+ .llm-modal-body .input-with-toggle .btn-icon {
1609
+ flex-shrink: 0;
1610
+ }
1611
+
1612
+ .llm-modal-footer {
1613
+ display: flex;
1614
+ justify-content: flex-end;
1615
+ gap: 8px;
1616
+ padding: 12px 20px;
1617
+ border-top: 1px solid var(--border);
1618
+ }
1619
+
1620
+ .llm-modal-footer .btn {
1621
+ padding: 8px 20px;
1622
+ font-size: 0.8rem;
1623
+ font-weight: 600;
1624
+ }
1625
+
1492
1626
  /* ============================================
1493
1627
  Auth Overlay (Login Screen)
1494
1628
  ============================================ */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fa-mcp-sdk",
3
3
  "productName": "FA MCP SDK",
4
- "version": "0.4.17",
4
+ "version": "0.4.19",
5
5
  "description": "Core infrastructure and templates for building Model Context Protocol (MCP) servers with TypeScript",
6
6
  "type": "module",
7
7
  "main": "dist/core/index.js",