fa-mcp-sdk 0.4.134 → 0.4.140

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.
@@ -58,7 +58,7 @@
58
58
  "dependencies": {
59
59
  "@modelcontextprotocol/sdk": "^1.29.0",
60
60
  "dotenv": "^17.4.1",
61
- "fa-mcp-sdk": "^0.4.124"
61
+ "fa-mcp-sdk": "^0.4.140"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@types/express": "^5.0.6",
@@ -30,6 +30,10 @@
30
30
  <label for="authToken">Token</label>
31
31
  <input type="password" id="authToken" placeholder="Enter access token" autocomplete="off" data-testid="at-auth-token-input">
32
32
  </div>
33
+ <label class="auth-remember" data-testid="at-auth-token-remember-label">
34
+ <input type="checkbox" id="authTokenRemember" checked data-testid="at-auth-token-remember">
35
+ <span>Remember me on this device</span>
36
+ </label>
33
37
  <button type="submit" class="btn btn-primary auth-submit" data-testid="at-auth-token-submit">
34
38
  <span class="material-icons-round">login</span>
35
39
  Sign In
@@ -45,6 +49,10 @@
45
49
  <label for="authPassword">Password</label>
46
50
  <input type="password" id="authPassword" placeholder="Password" autocomplete="current-password" data-testid="at-auth-password">
47
51
  </div>
52
+ <label class="auth-remember" data-testid="at-auth-basic-remember-label">
53
+ <input type="checkbox" id="authBasicRemember" checked data-testid="at-auth-basic-remember">
54
+ <span>Remember me on this device</span>
55
+ </label>
48
56
  <button type="submit" class="btn btn-primary auth-submit" data-testid="at-auth-basic-submit">
49
57
  <span class="material-icons-round">login</span>
50
58
  Sign In
@@ -363,6 +371,9 @@
363
371
  </div>
364
372
  </section>
365
373
  </div>
374
+ <div class="settings-modal-footer">
375
+ <button type="button" class="btn btn-primary" id="settingsModalOk" data-testid="at-settings-modal-ok">OK</button>
376
+ </div>
366
377
  </div>
367
378
  </div>
368
379
 
@@ -38,6 +38,8 @@ function apiFetch(url, options = {}) {
38
38
  * Auth manager — handles login overlay when agentTester.useAuth is enabled.
39
39
  */
40
40
  class AuthManager {
41
+ static LS_KEY = 'agentTesterAuthCreds';
42
+
41
43
  constructor() {
42
44
  this._authenticated = false;
43
45
  this._authRequired = false;
@@ -61,7 +63,13 @@ class AuthManager {
61
63
  return true;
62
64
  }
63
65
 
64
- this._showLoginOverlay(status.methods || []);
66
+ // Try silent re-login with saved credentials before showing overlay
67
+ const saved = this._loadSavedCreds();
68
+ if (saved && (await this._login(saved, { silent: true }))) {
69
+ return true;
70
+ }
71
+
72
+ this._showLoginOverlay(status.methods || [], saved);
65
73
  return false; // block app init until authenticated
66
74
  } catch (e) {
67
75
  console.warn('Auth status check failed, proceeding without auth:', e);
@@ -69,7 +77,39 @@ class AuthManager {
69
77
  }
70
78
  }
71
79
 
72
- _showLoginOverlay(methods) {
80
+ _loadSavedCreds() {
81
+ try {
82
+ const raw = localStorage.getItem(AuthManager.LS_KEY);
83
+ if (!raw) {
84
+ return null;
85
+ }
86
+ const obj = JSON.parse(raw);
87
+ if (obj && (obj.token || (obj.username && obj.password))) {
88
+ return obj;
89
+ }
90
+ } catch {
91
+ /* ignore */
92
+ }
93
+ return null;
94
+ }
95
+
96
+ _saveCreds(creds) {
97
+ try {
98
+ localStorage.setItem(AuthManager.LS_KEY, JSON.stringify(creds));
99
+ } catch {
100
+ /* ignore */
101
+ }
102
+ }
103
+
104
+ _clearSavedCreds() {
105
+ try {
106
+ localStorage.removeItem(AuthManager.LS_KEY);
107
+ } catch {
108
+ /* ignore */
109
+ }
110
+ }
111
+
112
+ _showLoginOverlay(methods, saved) {
73
113
  const overlay = document.getElementById('authOverlay');
74
114
  const appEl = document.querySelector('.app');
75
115
  overlay.style.display = 'flex';
@@ -82,10 +122,30 @@ class AuthManager {
82
122
  const basicForm = document.getElementById('authBasicForm');
83
123
  const tabs = document.getElementById('authTabs');
84
124
 
125
+ // Pre-fill from saved credentials (if any)
126
+ if (saved?.token) {
127
+ document.getElementById('authToken').value = saved.token;
128
+ }
129
+ if (saved?.username) {
130
+ document.getElementById('authUsername').value = saved.username;
131
+ }
132
+ if (saved?.password) {
133
+ document.getElementById('authPassword').value = saved.password;
134
+ }
135
+
136
+ // If saved creds match a single available method, switch to that tab.
137
+ const preferBasic = saved?.username && hasBasic && !saved?.token;
138
+
85
139
  if (hasToken && hasBasic) {
86
140
  tabs.style.display = 'flex';
87
- tokenForm.style.display = 'flex';
88
- basicForm.style.display = 'none';
141
+ if (preferBasic) {
142
+ tokenForm.style.display = 'none';
143
+ basicForm.style.display = 'flex';
144
+ document.querySelectorAll('.auth-tab').forEach((t) => t.classList.toggle('active', t.dataset.tab === 'basic'));
145
+ } else {
146
+ tokenForm.style.display = 'flex';
147
+ basicForm.style.display = 'none';
148
+ }
89
149
  this._bindTabs();
90
150
  } else if (hasBasic) {
91
151
  tokenForm.style.display = 'none';
@@ -98,8 +158,9 @@ class AuthManager {
98
158
  tokenForm.addEventListener('submit', (e) => {
99
159
  e.preventDefault();
100
160
  const token = document.getElementById('authToken').value.trim();
161
+ const remember = document.getElementById('authTokenRemember').checked;
101
162
  if (token) {
102
- this._login({ token });
163
+ this._login({ token }, { remember });
103
164
  }
104
165
  });
105
166
 
@@ -107,8 +168,9 @@ class AuthManager {
107
168
  e.preventDefault();
108
169
  const username = document.getElementById('authUsername').value.trim();
109
170
  const password = document.getElementById('authPassword').value;
171
+ const remember = document.getElementById('authBasicRemember').checked;
110
172
  if (username && password) {
111
- this._login({ username, password });
173
+ this._login({ username, password }, { remember });
112
174
  }
113
175
  });
114
176
  }
@@ -134,8 +196,17 @@ class AuthManager {
134
196
  });
135
197
  }
136
198
 
137
- async _login(credentials) {
138
- this._hideError();
199
+ /**
200
+ * Attempt login. Options:
201
+ * - silent: on failure, suppress error UI and return false (used for auto-login)
202
+ * - remember: on success, persist credentials to localStorage; on omission, leave LS untouched
203
+ *
204
+ * Returns true on success, false on failure.
205
+ */
206
+ async _login(credentials, { silent = false, remember } = {}) {
207
+ if (!silent) {
208
+ this._hideError();
209
+ }
139
210
  try {
140
211
  const resp = await apiFetch(`${API_BASE}/api/auth/login`, {
141
212
  method: 'POST',
@@ -144,13 +215,23 @@ class AuthManager {
144
215
  });
145
216
 
146
217
  if (!resp.ok) {
147
- const err = await resp.json();
218
+ if (silent) {
219
+ this._clearSavedCreds();
220
+ return false;
221
+ }
222
+ const err = await resp.json().catch(() => ({}));
148
223
  this._showError(err.error || 'Authentication failed');
149
- return;
224
+ return false;
150
225
  }
151
226
 
152
227
  this._authenticated = true;
153
228
 
229
+ if (remember === true) {
230
+ this._saveCreds(credentials);
231
+ } else if (remember === false) {
232
+ this._clearSavedCreds();
233
+ }
234
+
154
235
  // Hide overlay, show app
155
236
  document.getElementById('authOverlay').style.display = 'none';
156
237
  document.querySelector('.app').style.display = 'flex';
@@ -158,8 +239,12 @@ class AuthManager {
158
239
 
159
240
  // Initialize the main app after successful login
160
241
  window.mcpAgentTester = new McpAgentTester();
242
+ return true;
161
243
  } catch (_e) {
162
- this._showError('Connection error');
244
+ if (!silent) {
245
+ this._showError('Connection error');
246
+ }
247
+ return false;
163
248
  }
164
249
  }
165
250
 
@@ -172,6 +257,7 @@ class AuthManager {
172
257
  }
173
258
 
174
259
  async _logout() {
260
+ this._clearSavedCreds();
175
261
  try {
176
262
  await apiFetch(`${API_BASE}/api/auth/logout`, { method: 'POST' });
177
263
  } catch {
@@ -1216,6 +1302,7 @@ class McpAgentTester {
1216
1302
  this.settingsModal = document.getElementById('settingsModal');
1217
1303
  this.settingsBtn = document.getElementById('settingsBtn');
1218
1304
  this.settingsModalClose = document.getElementById('settingsModalClose');
1305
+ this.settingsModalOk = document.getElementById('settingsModalOk');
1219
1306
 
1220
1307
  // LLM settings — collapsed view + modal
1221
1308
  this.modelDisplay = document.getElementById('modelDisplay');
@@ -1375,12 +1462,8 @@ class McpAgentTester {
1375
1462
  if (this.settingsModalClose) {
1376
1463
  this.settingsModalClose.addEventListener('click', () => this.closeSettingsModal());
1377
1464
  }
1378
- if (this.settingsModal) {
1379
- this.settingsModal.addEventListener('click', (e) => {
1380
- if (e.target === this.settingsModal) {
1381
- this.closeSettingsModal();
1382
- }
1383
- });
1465
+ if (this.settingsModalOk) {
1466
+ this.settingsModalOk.addEventListener('click', () => this.closeSettingsModal());
1384
1467
  }
1385
1468
  document.addEventListener('keydown', (e) => {
1386
1469
  if (e.key === 'Escape' && this.settingsModal && this.settingsModal.style.display === 'flex') {
@@ -521,6 +521,15 @@ body {
521
521
  color: var(--text);
522
522
  }
523
523
 
524
+ #logoutBtn {
525
+ color: var(--error);
526
+ }
527
+
528
+ #logoutBtn:hover {
529
+ background-color: var(--error-bg);
530
+ color: var(--error);
531
+ }
532
+
524
533
  .btn-danger {
525
534
  background: var(--error-bg);
526
535
  color: var(--error);
@@ -2117,6 +2126,13 @@ textarea.prompt-modified {
2117
2126
  margin-bottom: 0;
2118
2127
  }
2119
2128
 
2129
+ .settings-modal-footer {
2130
+ display: flex;
2131
+ justify-content: flex-end;
2132
+ padding: 12px 20px;
2133
+ border-top: 1px solid var(--border);
2134
+ }
2135
+
2120
2136
  /* ============================================
2121
2137
  LLM Settings Modal
2122
2138
  ============================================ */
@@ -2334,6 +2350,24 @@ textarea.prompt-modified {
2334
2350
  box-shadow: 0 0 0 3px var(--primary-glow);
2335
2351
  }
2336
2352
 
2353
+ .auth-remember {
2354
+ display: flex;
2355
+ align-items: center;
2356
+ gap: 8px;
2357
+ font-size: 0.85rem;
2358
+ color: var(--text-secondary);
2359
+ cursor: pointer;
2360
+ user-select: none;
2361
+ }
2362
+
2363
+ .auth-remember input[type="checkbox"] {
2364
+ width: 16px;
2365
+ height: 16px;
2366
+ margin: 0;
2367
+ cursor: pointer;
2368
+ accent-color: var(--primary);
2369
+ }
2370
+
2337
2371
  .auth-submit {
2338
2372
  display: flex;
2339
2373
  align-items: center;
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.134",
4
+ "version": "0.4.140",
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",