@dollhousemcp/mcp-server 2.0.11 → 2.0.12-rc.2

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 (93) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/config/ConfigManager.d.ts +34 -0
  3. package/dist/config/ConfigManager.d.ts.map +1 -1
  4. package/dist/config/ConfigManager.js +46 -1
  5. package/dist/config/env.d.ts +18 -0
  6. package/dist/config/env.d.ts.map +1 -1
  7. package/dist/config/env.js +89 -2
  8. package/dist/di/Container.d.ts +1 -0
  9. package/dist/di/Container.d.ts.map +1 -1
  10. package/dist/di/Container.js +38 -2
  11. package/dist/elements/agents/AgentManager.js +2 -2
  12. package/dist/generated/version.d.ts +2 -2
  13. package/dist/generated/version.d.ts.map +1 -1
  14. package/dist/generated/version.js +3 -3
  15. package/dist/handlers/mcp-aql/GatekeeperSession.d.ts.map +1 -1
  16. package/dist/handlers/mcp-aql/GatekeeperSession.js +3 -2
  17. package/dist/handlers/mcp-aql/MCPAQLHandler.d.ts.map +1 -1
  18. package/dist/handlers/mcp-aql/MCPAQLHandler.js +11 -3
  19. package/dist/handlers/mcp-aql/OperationSchema.d.ts.map +1 -1
  20. package/dist/handlers/mcp-aql/OperationSchema.js +11 -6
  21. package/dist/handlers/mcp-aql/policies/ToolClassification.d.ts.map +1 -1
  22. package/dist/handlers/mcp-aql/policies/ToolClassification.js +8 -1
  23. package/dist/index.js +53 -3
  24. package/dist/security/InputNormalizer.d.ts +4 -6
  25. package/dist/security/InputNormalizer.d.ts.map +1 -1
  26. package/dist/security/InputNormalizer.js +10 -16
  27. package/dist/security/audit/config/suppressions.d.ts.map +1 -1
  28. package/dist/security/audit/config/suppressions.js +26 -1
  29. package/dist/security/constants.d.ts +7 -0
  30. package/dist/security/constants.d.ts.map +1 -1
  31. package/dist/security/constants.js +10 -1
  32. package/dist/security/contentValidator.d.ts.map +1 -1
  33. package/dist/security/contentValidator.js +14 -6
  34. package/dist/security/encryption/PatternEncryptor.d.ts.map +1 -1
  35. package/dist/security/encryption/PatternEncryptor.js +18 -10
  36. package/dist/security/securityMonitor.d.ts +1 -1
  37. package/dist/security/securityMonitor.d.ts.map +1 -1
  38. package/dist/security/securityMonitor.js +1 -1
  39. package/dist/security/tokenManager.d.ts +18 -2
  40. package/dist/security/tokenManager.d.ts.map +1 -1
  41. package/dist/security/tokenManager.js +47 -15
  42. package/dist/security/validators/unicodeValidator.d.ts +4 -5
  43. package/dist/security/validators/unicodeValidator.d.ts.map +1 -1
  44. package/dist/security/validators/unicodeValidator.js +19 -20
  45. package/dist/services/VerificationNotifier.d.ts.map +1 -1
  46. package/dist/services/VerificationNotifier.js +15 -13
  47. package/dist/services/validation/GenericElementValidator.js +5 -5
  48. package/dist/web/console/IngestRoutes.d.ts +14 -2
  49. package/dist/web/console/IngestRoutes.d.ts.map +1 -1
  50. package/dist/web/console/IngestRoutes.js +69 -5
  51. package/dist/web/console/LeaderElection.d.ts +37 -3
  52. package/dist/web/console/LeaderElection.d.ts.map +1 -1
  53. package/dist/web/console/LeaderElection.js +67 -7
  54. package/dist/web/console/LeaderForwardingSink.d.ts +19 -3
  55. package/dist/web/console/LeaderForwardingSink.d.ts.map +1 -1
  56. package/dist/web/console/LeaderForwardingSink.js +44 -9
  57. package/dist/web/console/PromotionManager.d.ts +29 -0
  58. package/dist/web/console/PromotionManager.d.ts.map +1 -0
  59. package/dist/web/console/PromotionManager.js +94 -0
  60. package/dist/web/console/SessionNames.d.ts +7 -0
  61. package/dist/web/console/SessionNames.d.ts.map +1 -1
  62. package/dist/web/console/SessionNames.js +10 -1
  63. package/dist/web/console/UnifiedConsole.d.ts +26 -1
  64. package/dist/web/console/UnifiedConsole.d.ts.map +1 -1
  65. package/dist/web/console/UnifiedConsole.js +143 -20
  66. package/dist/web/portDiscovery.d.ts +4 -3
  67. package/dist/web/portDiscovery.d.ts.map +1 -1
  68. package/dist/web/portDiscovery.js +7 -5
  69. package/dist/web/public/app.js +35 -18
  70. package/dist/web/public/consoleAuth.js +152 -0
  71. package/dist/web/public/index.html +243 -9
  72. package/dist/web/public/logs.js +1 -1
  73. package/dist/web/public/metrics.js +3 -3
  74. package/dist/web/public/permissions.js +1 -1
  75. package/dist/web/public/security.css +396 -0
  76. package/dist/web/public/security.js +393 -0
  77. package/dist/web/public/sessions.css +54 -4
  78. package/dist/web/public/sessions.js +46 -6
  79. package/dist/web/public/setup.css +439 -0
  80. package/dist/web/public/setup.js +490 -18
  81. package/dist/web/public/styles.css +10 -0
  82. package/dist/web/routes/permissionRoutes.d.ts.map +1 -1
  83. package/dist/web/routes/permissionRoutes.js +8 -3
  84. package/dist/web/routes/setupRoutes.d.ts +4 -0
  85. package/dist/web/routes/setupRoutes.d.ts.map +1 -1
  86. package/dist/web/routes/setupRoutes.js +418 -7
  87. package/dist/web/routes.d.ts.map +1 -1
  88. package/dist/web/routes.js +9 -5
  89. package/dist/web/server.d.ts +49 -3
  90. package/dist/web/server.d.ts.map +1 -1
  91. package/dist/web/server.js +282 -82
  92. package/package.json +11 -2
  93. package/server.json +2 -2
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Console authentication helper (#1780).
3
+ *
4
+ * Reads the session token from the <meta name="dollhouse-console-token"> tag
5
+ * injected by the server at page-load time, and provides small wrapper
6
+ * functions that attach the token to API fetches and EventSource streams.
7
+ *
8
+ * When the token is empty (auth feature flag off, or server older than #1780)
9
+ * the helpers fall back to plain fetch / EventSource with no auth header —
10
+ * backwards-compatible with any Phase 0 setup.
11
+ *
12
+ * Usage (replace existing calls in other JS files):
13
+ * fetch('/api/elements') → DollhouseAuth.apiFetch('/api/elements')
14
+ * new EventSource('/api/logs/stream') → DollhouseAuth.apiEventSource('/api/logs/stream')
15
+ *
16
+ * External URLs (github.com, cdn.jsdelivr.net, etc.) should continue to use
17
+ * plain fetch — never send the console token to non-local hosts.
18
+ *
19
+ * @since v2.1.0 — Issue #1780
20
+ */
21
+
22
+ (function () {
23
+ 'use strict';
24
+
25
+ /**
26
+ * Strict format for console tokens — 64 lowercase hex characters.
27
+ * We refuse to attach anything that doesn't match this pattern, so
28
+ * malformed meta values or Unicode-obfuscated content never reach the
29
+ * server or leak into request URLs. DMCP-SEC-004 mitigation.
30
+ */
31
+ var TOKEN_FORMAT = /^[0-9a-f]{64}$/;
32
+
33
+ /**
34
+ * Read the console token from the meta tag in the document head.
35
+ * Normalizes the value to NFC and validates against the strict hex format.
36
+ * Returns an empty string if the tag is absent, empty, still the raw
37
+ * template placeholder, or fails validation.
38
+ */
39
+ function readTokenFromMeta() {
40
+ var meta = document.querySelector('meta[name="dollhouse-console-token"]');
41
+ if (!meta) return '';
42
+ var raw = (meta.getAttribute('content') || '').trim();
43
+ if (!raw || raw === '{{CONSOLE_TOKEN}}') return '';
44
+ // Normalize to NFC — strips any zero-width / combining mark weirdness
45
+ // before the format check. For legitimate hex tokens this is a no-op.
46
+ var normalized = raw.normalize('NFC');
47
+ if (!TOKEN_FORMAT.test(normalized)) return '';
48
+ return normalized;
49
+ }
50
+
51
+ var consoleToken = readTokenFromMeta();
52
+
53
+ /**
54
+ * Fire a custom event when the server returns 401 — the cached token is
55
+ * stale (rotated, server restarted, file deleted). The global listener
56
+ * in app.js shows a reload toast. Dispatched at most once per page load
57
+ * so multiple 401s don't stack multiple events.
58
+ */
59
+ var sessionExpiredFired = false;
60
+ function fireSessionExpired() {
61
+ if (sessionExpiredFired) return;
62
+ sessionExpiredFired = true;
63
+ window.dispatchEvent(new CustomEvent('dollhouse:session-expired'));
64
+ }
65
+
66
+ /**
67
+ * Fetch wrapper that attaches Authorization: Bearer to API requests.
68
+ * Accepts the same arguments as native fetch().
69
+ *
70
+ * @param {RequestInfo} input - URL or Request object
71
+ * @param {RequestInit} [init] - Fetch options (body, method, headers, etc.)
72
+ * @returns {Promise<Response>}
73
+ */
74
+ function apiFetch(input, init) {
75
+ if (!consoleToken) {
76
+ return fetch(input, init);
77
+ }
78
+ const opts = init ? Object.assign({}, init) : {};
79
+ const headers = new Headers(opts.headers || {});
80
+ if (!headers.has('Authorization')) {
81
+ headers.set('Authorization', 'Bearer ' + consoleToken);
82
+ }
83
+ opts.headers = headers;
84
+ return fetch(input, opts).then(function (response) {
85
+ if (response.status === 401 && consoleToken) {
86
+ fireSessionExpired();
87
+ }
88
+ return response;
89
+ });
90
+ }
91
+
92
+ /**
93
+ * EventSource wrapper that appends ?token=<token> to the URL.
94
+ * EventSource cannot set custom headers, so the token is carried as a
95
+ * query parameter. The middleware on the server accepts both the header
96
+ * and this fallback.
97
+ *
98
+ * @param {string} url - Relative or absolute URL
99
+ * @param {EventSourceInit} [init] - EventSource options (withCredentials, etc.)
100
+ * @returns {EventSource}
101
+ */
102
+ function apiEventSource(url, init) {
103
+ if (!consoleToken) {
104
+ return new EventSource(url, init);
105
+ }
106
+ var separator = url.indexOf('?') >= 0 ? '&' : '?';
107
+ var urlWithToken = url + separator + 'token=' + encodeURIComponent(consoleToken);
108
+ var es = new EventSource(urlWithToken, init);
109
+ // EventSource doesn't expose HTTP status codes on error. When the
110
+ // connection closes with an error, probe the base URL with a HEAD
111
+ // request to detect 401. If the token is stale, apiFetch's .then()
112
+ // handler fires the session-expired event. addEventListener runs
113
+ // alongside any onerror the caller sets — no interference.
114
+ es.addEventListener('error', function () {
115
+ if (consoleToken && es.readyState === EventSource.CLOSED) {
116
+ apiFetch(url, { method: 'HEAD' }).catch(function () { /* ignore */ });
117
+ }
118
+ });
119
+ return es;
120
+ }
121
+
122
+ /** Expose the helpers on the global namespace. */
123
+ window.DollhouseAuth = {
124
+ /** Current console token value (empty string if auth is off). */
125
+ get token() { return consoleToken; },
126
+
127
+ /**
128
+ * Update the cached token. If an explicit token string is provided and
129
+ * passes the strict hex format check, the in-memory cache is updated
130
+ * directly — this is the path used after a rotation response so the
131
+ * active tab picks up the new token without a page reload. Without an
132
+ * argument, falls back to re-reading the meta tag (legacy behavior).
133
+ *
134
+ * @param {string} [explicitToken] - New token value from a rotation response.
135
+ * @returns {string} The token now in use (may be empty if auth is off).
136
+ */
137
+ refresh: function (explicitToken) {
138
+ if (typeof explicitToken === 'string') {
139
+ var normalized = explicitToken.normalize('NFC');
140
+ if (TOKEN_FORMAT.test(normalized)) {
141
+ consoleToken = normalized;
142
+ return consoleToken;
143
+ }
144
+ }
145
+ consoleToken = readTokenFromMeta();
146
+ return consoleToken;
147
+ },
148
+
149
+ apiFetch: apiFetch,
150
+ apiEventSource: apiEventSource,
151
+ };
152
+ })();
@@ -7,6 +7,11 @@
7
7
  <meta name="description" content="DollhouseMCP management console — portfolio, logs, metrics">
8
8
  <meta http-equiv="X-Content-Type-Options" content="nosniff">
9
9
  <meta http-equiv="X-Frame-Options" content="DENY">
10
+ <!-- Console session token (#1780) — injected by server at request time.
11
+ When auth is enabled, browser JS reads this tag and attaches the value
12
+ as an Authorization: Bearer header on fetch calls, or as a ?token= query
13
+ param on EventSource connections. An empty value means auth is off. -->
14
+ <meta name="dollhouse-console-token" content="{{CONSOLE_TOKEN}}">
10
15
  <link rel="stylesheet" href="fonts.css">
11
16
  <link rel="stylesheet" href="styles.css">
12
17
  <link rel="stylesheet" href="logs.css">
@@ -14,6 +19,7 @@
14
19
  <link rel="stylesheet" href="permissions.css">
15
20
  <link rel="stylesheet" href="sessions.css">
16
21
  <link rel="stylesheet" href="setup.css">
22
+ <link rel="stylesheet" href="security.css">
17
23
  <!-- uPlot for metrics time-series charts -->
18
24
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uplot@1.6.30/dist/uPlot.min.css" integrity="sha384-IfV0B7MIOYuO95kO9G5ySKPz/85zqFNOAs8iy4tkK5zd9izhJAB8b7lHrwYqqmYE" crossorigin="anonymous">
19
25
  </head>
@@ -32,10 +38,11 @@
32
38
  <div class="header-right">
33
39
  <nav class="console-tabs" id="console-tabs" aria-label="Console tabs">
34
40
  <button class="console-tab" data-tab="setup">Setup</button>
35
- <button class="console-tab active" data-tab="portfolio">Portfolio</button>
41
+ <button class="console-tab" data-tab="security">Auth</button>
36
42
  <button class="console-tab" data-tab="logs">Logs</button>
37
43
  <button class="console-tab" data-tab="metrics">Metrics</button>
38
44
  <button class="console-tab" data-tab="permissions">Permissions</button>
45
+ <button class="console-tab active" data-tab="portfolio">Portfolio</button>
39
46
  </nav>
40
47
  <div class="session-indicator" id="session-indicator" title="Active sessions"></div>
41
48
  <div class="site-stats" id="stats" aria-live="polite"></div>
@@ -54,8 +61,8 @@
54
61
  <span class="setup-status-dot"></span>
55
62
  <span>DollhouseMCP is running on your machine</span>
56
63
  </div>
57
- <h2 class="setup-hero-title">Connect your AI client</h2>
58
- <p class="setup-hero-sub">Pick your platform below. Click <strong>Install Now</strong> to auto-configure, or copy the config manually.</p>
64
+ <h2 class="setup-hero-title">Connect DollhouseMCP to your AI client</h2>
65
+ <p class="setup-hero-sub">Pick your platform below. Click <strong>Configure Now</strong> to connect, or copy the config manually.</p>
59
66
  </div>
60
67
 
61
68
  <fieldset class="setup-method-toggle" id="setup-method-toggle">
@@ -70,6 +77,16 @@
70
77
  </button>
71
78
  </fieldset>
72
79
 
80
+ <fieldset class="setup-channel-toggle" id="setup-channel-toggle">
81
+ <legend class="setup-channel-legend">Release channel</legend>
82
+ <select id="setup-channel-select" class="setup-channel-select" aria-describedby="setup-channel-hint">
83
+ <option value="latest" selected>Stable</option>
84
+ <option value="rc">Release Candidate</option>
85
+ <option value="beta">Beta</option>
86
+ </select>
87
+ <span class="setup-channel-hint" id="setup-channel-hint">Recommended for most users.</span>
88
+ </fieldset>
89
+
73
90
  <!-- Pinned install prereq (hidden by default, shown when pinned selected) -->
74
91
  <div class="setup-pinned-prereq" id="setup-pinned-prereq" hidden>
75
92
  <div class="setup-method">
@@ -115,10 +132,10 @@ npm install @dollhousemcp/mcp-server</code></pre>
115
132
  <!-- Claude Desktop -->
116
133
  <section class="setup-panel is-active" role="tabpanel" id="setup-panel-claude-desktop" aria-labelledby="setup-tab-claude-desktop">
117
134
  <div class="setup-method setup-method-primary">
118
- <h3>Auto-install <span class="setup-method-badge setup-badge">auto-updating</span></h3>
135
+ <h3>Auto-configure <span class="setup-method-badge setup-badge">auto-updating</span></h3>
119
136
  <p class="setup-method-desc">Pulls the latest version of DollhouseMCP on every startup. Uses <code>npx @latest</code> under the hood. Restart Claude Desktop after.</p>
120
137
  <div class="setup-install-row">
121
- <button class="setup-btn setup-btn-primary setup-install-btn" type="button" data-install-client="claude">Install Now</button>
138
+ <button class="setup-btn setup-btn-primary setup-install-btn" type="button" data-install-client="claude">Configure Now</button>
122
139
  <span class="setup-install-status" data-install-status="claude"></span>
123
140
  </div>
124
141
  </div>
@@ -155,10 +172,10 @@ npm install @dollhousemcp/mcp-server</code></pre>
155
172
  <!-- Claude Code -->
156
173
  <section class="setup-panel" role="tabpanel" id="setup-panel-claude-code" aria-labelledby="setup-tab-claude-code" hidden>
157
174
  <div class="setup-method setup-method-primary">
158
- <h3>Auto-install <span class="setup-method-badge setup-badge">auto-updating</span></h3>
175
+ <h3>Auto-configure <span class="setup-method-badge setup-badge">auto-updating</span></h3>
159
176
  <p class="setup-method-desc">Adds DollhouseMCP to Claude Code, pulling the latest version on every startup.</p>
160
177
  <div class="setup-install-row">
161
- <button class="setup-btn setup-btn-primary setup-install-btn" type="button" data-install-client="claude-code">Install Now</button>
178
+ <button class="setup-btn setup-btn-primary setup-install-btn" type="button" data-install-client="claude-code">Configure Now</button>
162
179
  <span class="setup-install-status" data-install-status="claude-code"></span>
163
180
  </div>
164
181
  </div>
@@ -209,11 +226,163 @@ npm install @dollhousemcp/mcp-server</code></pre>
209
226
  </div>
210
227
  </div>
211
228
 
229
+ <!-- License selector (#1746 Step 3) -->
230
+ <div class="license-section" id="license-section">
231
+ <h3 class="license-heading">Choose your license</h3>
232
+ <p class="license-sub">DollhouseMCP is free and open source. Commercial use has simple, fair terms.</p>
233
+
234
+ <div class="license-tiers" id="license-tiers">
235
+ <!-- AGPL -->
236
+ <button class="license-tier is-selected" data-tier="agpl" type="button">
237
+ <span class="license-tier-price-label license-tier-price-label--free">Free</span>
238
+ <div class="license-tier-header">
239
+ <span class="license-tier-radio"></span>
240
+ <div>
241
+ <strong class="license-tier-name">AGPL-3.0</strong>
242
+ <span class="license-tier-badge license-tier-badge--default">default</span>
243
+ </div>
244
+ </div>
245
+ <p class="license-tier-desc">Open source. Share-alike license. No sign-up, no telemetry required.</p>
246
+ </button>
247
+
248
+ <!-- Free Commercial -->
249
+ <button class="license-tier" data-tier="free-commercial" type="button">
250
+ <span class="license-tier-price-label license-tier-price-label--free">Free</span>
251
+ <div class="license-tier-header">
252
+ <span class="license-tier-radio"></span>
253
+ <div>
254
+ <strong class="license-tier-name">Commercial</strong>
255
+ </div>
256
+ </div>
257
+ <p class="license-tier-desc">For companies earning under $1 million annual revenue. Attribution required.</p>
258
+ </button>
259
+
260
+ <!-- Paid Commercial / Enterprise -->
261
+ <button class="license-tier" data-tier="paid-commercial" type="button">
262
+ <span class="license-tier-price-label license-tier-price-label--paid">Paid</span>
263
+ <div class="license-tier-header">
264
+ <span class="license-tier-radio"></span>
265
+ <div>
266
+ <strong class="license-tier-name">Enterprise</strong>
267
+ </div>
268
+ </div>
269
+ <p class="license-tier-desc">For companies over $1M annual revenue, or white label. Priority support and SLA options available.</p>
270
+ </button>
271
+ </div>
272
+
273
+ <!-- AGPL detail (shown by default) -->
274
+ <div class="license-detail" id="license-detail-agpl">
275
+ <div class="license-detail-content">
276
+ <p>You're using DollhouseMCP under the <a href="https://www.gnu.org/licenses/agpl-3.0.en.html" target="_blank" rel="noopener">GNU Affero General Public License v3</a>. No action needed &mdash; this is the default.</p>
277
+ <ul class="license-detail-list">
278
+ <li>Use, modify, and distribute freely</li>
279
+ <li>Derivative works must also be AGPL-licensed</li>
280
+ <li>Source code must be made available to users who interact with the software over a network</li>
281
+ <li>Original copyright and license notices must be preserved</li>
282
+ <li>No telemetry collection</li>
283
+ <li>Community support via GitHub</li>
284
+ </ul>
285
+ </div>
286
+ </div>
287
+
288
+ <!-- Free Commercial detail (hidden) -->
289
+ <div class="license-detail" id="license-detail-free-commercial" hidden>
290
+ <form class="license-form" id="license-form-free-commercial" autocomplete="off">
291
+ <label class="license-form-label">
292
+ Email address
293
+ <input class="license-form-input" type="email" name="email" required maxlength="254" placeholder="you@company.com" />
294
+ <span class="license-form-hint">Your license confirmation will be sent to this address. A valid email is required to receive your license.</span>
295
+ </label>
296
+ <label class="license-form-check">
297
+ <input type="checkbox" name="telemetry" required />
298
+ <span>I acknowledge that anonymous usage telemetry will be collected as a condition of this license</span>
299
+ </label>
300
+ <label class="license-form-check">
301
+ <input type="checkbox" name="attribution" required />
302
+ <span>I acknowledge I will provide attribution when DollhouseMCP is used in any commercial product</span>
303
+ </label>
304
+ <label class="license-form-check">
305
+ <input type="checkbox" name="attestation" required />
306
+ <span>I attest that my organization's annual revenue is under $1,000,000 USD</span>
307
+ </label>
308
+ <button class="setup-btn setup-btn-primary license-submit-btn" type="submit">Activate Commercial License</button>
309
+ <p class="license-form-status" id="license-status-free-commercial"></p>
310
+ </form>
311
+ </div>
312
+
313
+ <!-- Paid Commercial detail (hidden) -->
314
+ <div class="license-detail" id="license-detail-paid-commercial" hidden>
315
+ <form class="license-form" id="license-form-paid-commercial" autocomplete="off">
316
+ <label class="license-form-label">
317
+ Email address
318
+ <input class="license-form-input" type="email" name="email" required maxlength="254" placeholder="you@company.com" />
319
+ <span class="license-form-hint">Your license confirmation will be sent to this address. A valid email is required to receive your license.</span>
320
+ </label>
321
+ <label class="license-form-label">
322
+ Revenue scale
323
+ <select class="license-form-select" name="revenueScale" required>
324
+ <option value="" disabled selected>Select range</option>
325
+ <option value="$1M–$5M">$1M – $5M</option>
326
+ <option value="$5M–$25M">$5M – $25M</option>
327
+ <option value="$25M–$100M">$25M – $100M</option>
328
+ <option value="$100M+">$100M+</option>
329
+ </select>
330
+ </label>
331
+ <label class="license-form-label">
332
+ Company name
333
+ <input class="license-form-input" type="text" name="companyName" required maxlength="200" placeholder="Acme Corp" />
334
+ </label>
335
+ <label class="license-form-label">
336
+ Use case
337
+ <textarea class="license-form-textarea" name="useCase" rows="2" required maxlength="500" placeholder="Brief description of how you're using DollhouseMCP"></textarea>
338
+ </label>
339
+ <label class="license-form-check">
340
+ <input type="checkbox" name="telemetry" required />
341
+ <span>I acknowledge that anonymous usage telemetry will be collected as a condition of this license</span>
342
+ </label>
343
+ <button class="setup-btn setup-btn-primary license-submit-btn" type="submit">Send Inquiry</button>
344
+ <p class="license-form-status" id="license-status-paid-commercial"></p>
345
+ </form>
346
+ </div>
347
+
348
+ <!-- Verification code input (shown after commercial form submit) -->
349
+ <div class="license-detail license-verification" id="license-verification" hidden>
350
+ <form class="license-form" id="license-verify-form" autocomplete="off">
351
+ <p class="license-verify-intro">We sent a 6-digit verification code to <strong id="license-verify-email"></strong>.</p>
352
+ <label class="license-form-label">
353
+ Verification code
354
+ <input class="license-form-input license-code-input" type="text" name="code" required maxlength="6" pattern="\d{6}" placeholder="000000" inputmode="numeric" autocomplete="one-time-code" />
355
+ </label>
356
+ <div class="license-verify-actions">
357
+ <button class="setup-btn setup-btn-primary license-submit-btn" type="submit">Verify</button>
358
+ <button class="setup-btn setup-btn-secondary" type="button" id="license-resend-btn">Resend code</button>
359
+ </div>
360
+ <p class="license-form-status" id="license-verify-status"></p>
361
+ <p class="license-verify-timer" id="license-verify-timer"></p>
362
+ </form>
363
+ </div>
364
+
365
+ <!-- Saved confirmation (shown after successful save) -->
366
+ <div class="license-saved" id="license-saved" hidden>
367
+ <span class="license-saved-icon">&#10003;</span>
368
+ <span class="license-saved-text" id="license-saved-text">License saved</span>
369
+ </div>
370
+
371
+ <!-- License details (shown when a commercial license is active) -->
372
+ <div class="license-detail license-active-details" id="license-active-details" hidden>
373
+ <div class="license-detail-content">
374
+ <h4 style="margin: 0 0 12px;">Your license</h4>
375
+ <table class="license-info-table" id="license-info-table"><thead><tr><th>Field</th><th>Value</th></tr></thead><tbody id="license-info-tbody"></tbody></table>
376
+ <p class="license-file-path">License file: <code>~/.dollhouse/license.json</code></p>
377
+ </div>
378
+ </div>
379
+ </div>
380
+
212
381
  <div class="setup-footer">
213
382
  <p>Full documentation for all platforms (Continue, Docker, local LLMs) in the
214
383
  <a href="https://github.com/DollhouseMCP/mcp-server/blob/main/docs/guides/quick-start.md" target="_blank" rel="noopener">quick start guide</a>.
215
384
  </p>
216
- <p>Auto-install powered by <a href="https://github.com/supermemoryai/install-mcp" target="_blank" rel="noopener">install-mcp</a> by <a href="https://github.com/dhravyashah" target="_blank" rel="noopener">Dhravya Shah</a>.</p>
385
+ <p>Auto-configure powered by <a href="https://github.com/supermemoryai/install-mcp" target="_blank" rel="noopener">install-mcp</a> by <a href="https://github.com/dhravyashah" target="_blank" rel="noopener">Dhravya Shah</a>.</p>
217
386
  </div>
218
387
  </div>
219
388
  </div>
@@ -292,6 +461,66 @@ npm install @dollhousemcp/mcp-server</code></pre>
292
461
  <div id="permissions-dashboard-root"></div>
293
462
  </div>
294
463
 
464
+ <!-- Tab: Security (#1791) -->
465
+ <div id="tab-security" class="tab-panel" hidden>
466
+ <div id="security-dashboard-root"></div>
467
+ </div>
468
+
469
+ <!-- Auth tab intro content (#1816) — edit here, not in security.js -->
470
+ <template id="sec-intro-template">
471
+ <section class="sec-card sec-card--intro" data-collapsed="true" aria-labelledby="sec-intro-title">
472
+ <button class="sec-card-header" type="button" aria-expanded="false">
473
+ <h3 class="sec-card-title" id="sec-intro-title">Console Authentication
474
+ <span class="sec-intro-summary">&mdash; how it can protect your sessions, permissions, and configurations</span>
475
+ </h3>
476
+ <span class="sec-card-toggle" aria-hidden="true">&#9662;</span>
477
+ </button>
478
+ <div class="sec-card-body">
479
+ <h4 class="sec-intro-subtitle">Why this matters</h4>
480
+ <ul class="sec-intro-list">
481
+ <li><strong>Prompt injection defense</strong> &mdash; Your LLM
482
+ processes content from the outside world: web searches, emails,
483
+ documents, links. Any of these can carry hidden instructions
484
+ targeting DollhouseMCP &mdash; attempting to create malicious
485
+ elements, alter permissions, or change your configuration.
486
+ Authentication ensures those attempts fail. The attacker
487
+ doesn&rsquo;t have your token, and TOTP means sensitive
488
+ operations require a code from your phone that no injection
489
+ can reach.</li>
490
+ <li><strong>Defense in depth</strong> &mdash; DollhouseMCP
491
+ validates and sanitizes inputs at the server level, but no
492
+ single layer is foolproof. Authentication adds an independent
493
+ barrier: even if an attack gets past input validation, it still
494
+ can&rsquo;t perform privileged operations without your
495
+ credentials.</li>
496
+ <li><strong>Shared environments</strong> &mdash; On shared
497
+ workstations, multi-user servers, or containers with mapped
498
+ ports, authentication prevents other users or network-adjacent
499
+ processes from accessing this console or any of its
500
+ capabilities.</li>
501
+ </ul>
502
+
503
+ <h4 class="sec-intro-subtitle">How it works</h4>
504
+ <ul class="sec-intro-list">
505
+ <li><strong>Console Token</strong> &mdash; A secret key generated
506
+ on first run. Every API request must carry it. Without the token,
507
+ other processes on your machine can&rsquo;t touch the console.
508
+ Use the <strong>Copy</strong> button below to share the token
509
+ with external services, scripts, or tools that need to access
510
+ the DollhouseMCP API on your behalf.</li>
511
+ <li><strong>Authenticator (TOTP)</strong> &mdash; An optional second
512
+ factor from your phone. Even if the token leaks, sensitive actions
513
+ like rotation still need a 6-digit code from your authenticator
514
+ app that changes every 30 seconds.</li>
515
+ </ul>
516
+
517
+ <a class="sec-intro-link"
518
+ href="https://github.com/DollhouseMCP/mcp-server/blob/main/docs/guides/console-auth.md"
519
+ target="_blank" rel="noopener">Full documentation &rarr;</a>
520
+ </div>
521
+ </section>
522
+ </template>
523
+
295
524
  <dialog id="element-modal" class="modal" aria-labelledby="modal-title">
296
525
  <div class="modal-overlay" id="modal-overlay"></div>
297
526
  <div class="modal-dialog">
@@ -326,10 +555,11 @@ npm install @dollhousemcp/mcp-server</code></pre>
326
555
 
327
556
  <footer class="site-footer">
328
557
  <div class="footer-inner">
329
- <a href="https://github.com/DollhouseMCP/collection" class="footer-link">GitHub Repository</a>
558
+ <a href="https://github.com/DollhouseMCP/mcp-server" class="footer-link">GitHub Repository</a>
330
559
  <a href="./collection-index.json" class="footer-link">JSON API</a>
331
560
  <a href="https://dollhousemcp.com" class="footer-link">DollhouseMCP</a>
332
561
  <span class="footer-updated" id="footer-updated"></span>
562
+ <span class="footer-copyright">&copy; 2026 DollhouseMCP</span>
333
563
  </div>
334
564
  </footer>
335
565
 
@@ -358,11 +588,15 @@ npm install @dollhousemcp/mcp-server</code></pre>
358
588
  id="hljs-theme-dark" disabled>
359
589
  <!-- uPlot for metrics charts -->
360
590
  <script src="https://cdn.jsdelivr.net/npm/uplot@1.6.30/dist/uPlot.iife.min.js" integrity="sha384-1NEYi76CBpge3gahk4+X4M4JzdOV3WYq84RnByqYdAd5SdvJBTNCPFh/nsoHfN6i" crossorigin="anonymous"></script>
591
+ <!-- Console auth helper must load first — it reads the token meta tag and
592
+ exposes window.DollhouseAuth for all subsequent scripts (#1780). -->
593
+ <script src="consoleAuth.js"></script>
361
594
  <script src="setup.js"></script>
362
595
  <script src="app.js"></script>
363
596
  <script src="logs.js"></script>
364
597
  <script src="metrics.js"></script>
365
598
  <script src="permissions.js"></script>
366
599
  <script src="sessions.js"></script>
600
+ <script src="security.js"></script>
367
601
  </body>
368
602
  </html>
@@ -305,7 +305,7 @@
305
305
  if (filterCategory) params.set('category', filterCategory);
306
306
  if (filterLevel) params.set('level', filterLevel);
307
307
  const qs = params.toString();
308
- eventSource = new EventSource('/api/logs/stream' + (qs ? '?' + qs : ''));
308
+ eventSource = DollhouseAuth.apiEventSource('/api/logs/stream' + (qs ? '?' + qs : ''));
309
309
 
310
310
  eventSource.onopen = () => setStatus('connected');
311
311
 
@@ -147,7 +147,7 @@
147
147
  // ── Data fetching ────────────────────────────────────────────────────────
148
148
  async function fetchLatest() {
149
149
  try {
150
- const res = await fetch('/api/metrics?latest=true');
150
+ const res = await DollhouseAuth.apiFetch('/api/metrics?latest=true');
151
151
  if (!res.ok) return;
152
152
  const data = await res.json();
153
153
  if (data.snapshots?.length > 0) {
@@ -167,7 +167,7 @@
167
167
  async function fetchHistory() {
168
168
  try {
169
169
  const since = new Date(Date.now() - TIME_RANGES[activeRange]).toISOString();
170
- const res = await fetch(`/api/metrics?latest=false&since=${since}&limit=100`);
170
+ const res = await DollhouseAuth.apiFetch(`/api/metrics?latest=false&since=${since}&limit=100`);
171
171
  if (!res.ok) return;
172
172
  const data = await res.json();
173
173
  if (data.snapshots) {
@@ -403,7 +403,7 @@
403
403
  if (securityEventsCache && (now - securityEventsCacheTime) < SECURITY_CACHE_TTL) {
404
404
  renderSecurityEvents(securityEventsCache);
405
405
  } else {
406
- fetch('/api/logs?category=security&level=warn&limit=5')
406
+ DollhouseAuth.apiFetch('/api/logs?category=security&level=warn&limit=5')
407
407
  .then(r => r.ok ? r.json() : null)
408
408
  .then(data => {
409
409
  if (data?.entries) {
@@ -70,7 +70,7 @@
70
70
 
71
71
  async function poll() {
72
72
  try {
73
- const res = await fetch('/api/permissions/status');
73
+ const res = await DollhouseAuth.apiFetch('/api/permissions/status');
74
74
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
75
75
  const data = await res.json();
76
76
  render(data);