@egain/egain-mcp-server 1.0.1 → 1.0.4

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.
package/bin/mcp-server.js CHANGED
@@ -4046,9 +4046,9 @@ var init_config = __esm(() => {
4046
4046
  SDK_METADATA = {
4047
4047
  language: "typescript",
4048
4048
  openapiDocVersion: "1.0.0",
4049
- sdkVersion: "1.0.1",
4049
+ sdkVersion: "1.0.4",
4050
4050
  genVersion: "2.723.8",
4051
- userAgent: "speakeasy-sdk/mcp-typescript 1.0.1 2.723.8 1.0.0 @egain/egain-mcp-server"
4051
+ userAgent: "speakeasy-sdk/mcp-typescript 1.0.4 2.723.8 1.0.0 @egain/egain-mcp-server"
4052
4052
  };
4053
4053
  });
4054
4054
 
@@ -40910,6 +40910,9 @@ class AuthenticationHook {
40910
40910
  return {};
40911
40911
  }
40912
40912
  getSafariWarningPage() {
40913
+ if (SAFARI_WARNING_HTML) {
40914
+ return SAFARI_WARNING_HTML;
40915
+ }
40913
40916
  try {
40914
40917
  const projectRoot = getProjectRoot();
40915
40918
  const htmlPath = path.join(projectRoot, "src", "hooks", "auth-pages", "safari-warning.html");
@@ -40920,6 +40923,9 @@ class AuthenticationHook {
40920
40923
  }
40921
40924
  }
40922
40925
  getConfigPage() {
40926
+ if (CONFIG_PAGE_HTML) {
40927
+ return CONFIG_PAGE_HTML;
40928
+ }
40923
40929
  try {
40924
40930
  const projectRoot = getProjectRoot();
40925
40931
  const htmlPath = path.join(projectRoot, "src", "hooks", "auth-pages", "config-page.html");
@@ -40930,6 +40936,9 @@ class AuthenticationHook {
40930
40936
  }
40931
40937
  }
40932
40938
  getConfigPageJS() {
40939
+ if (CONFIG_PAGE_JS) {
40940
+ return CONFIG_PAGE_JS;
40941
+ }
40933
40942
  try {
40934
40943
  const projectRoot = getProjectRoot();
40935
40944
  const jsPath = path.join(projectRoot, "src", "hooks", "auth-pages", "config-page.js");
@@ -41867,7 +41876,1135 @@ var __filename2, __dirname2, getProjectRoot = () => {
41867
41876
  return configDir;
41868
41877
  }, getConfigPath = () => {
41869
41878
  return path.join(getConfigDir(), "config.json");
41870
- }, execAsync, CONFIG_SERVER_PORT = 3333, CONFIG_SERVER_HOST = "localhost";
41879
+ }, execAsync, CONFIG_SERVER_PORT = 3333, CONFIG_SERVER_HOST = "localhost", CONFIG_PAGE_HTML = `<!DOCTYPE html>
41880
+ <html lang="en">
41881
+ <head>
41882
+ <meta charset="UTF-8">
41883
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
41884
+ <title>eGain MCP - Sign In</title>
41885
+ <style>
41886
+ * { margin: 0; padding: 0; box-sizing: border-box; }
41887
+ body {
41888
+ font-family: "Open Sans", "Segoe UI", "SegoeUI", "Helvetica Neue", Helvetica, Arial, sans-serif !important;
41889
+ background: #fef1fd;
41890
+ min-height: 100vh;
41891
+ display: flex;
41892
+ justify-content: center;
41893
+ align-items: center;
41894
+ padding: 20px;
41895
+ }
41896
+ .container {
41897
+ background: white;
41898
+ border-radius: 16px;
41899
+ box-shadow: 0px 0px 30px 0px rgba(0, 0, 0, 0.12);
41900
+ max-width: 500px;
41901
+ width: 100%;
41902
+ padding: 32px;
41903
+ }
41904
+ h1 { color: #333; margin-bottom: 8px; font-size: 24px; }
41905
+ .subtitle { color: #666; margin-bottom: 20px; font-size: 13px; }
41906
+ .quick-signin {
41907
+ display: none;
41908
+ text-align: center;
41909
+ position: relative;
41910
+ }
41911
+ .quick-signin .icon { font-size: 64px; }
41912
+ .quick-signin h1 { text-align: center; }
41913
+ .saved-config {
41914
+ background: #f8f9fa;
41915
+ border-radius: 8px;
41916
+ padding: 20px;
41917
+ margin: 30px 0;
41918
+ text-align: left;
41919
+ }
41920
+ .saved-config-title {
41921
+ font-size: 12px;
41922
+ font-weight: 600;
41923
+ color: #666;
41924
+ text-transform: uppercase;
41925
+ margin-bottom: 15px;
41926
+ text-align: center;
41927
+ }
41928
+ .config-item {
41929
+ display: flex;
41930
+ justify-content: space-between;
41931
+ padding: 8px 0;
41932
+ border-bottom: 1px solid #e1e4e8;
41933
+ font-size: 13px;
41934
+ }
41935
+ .config-item:last-child { border-bottom: none; }
41936
+ .config-label { color: #666; font-weight: 500; }
41937
+ .config-value {
41938
+ color: #333;
41939
+ font-family: 'Courier New', monospace;
41940
+ max-width: 300px;
41941
+ overflow: hidden;
41942
+ text-overflow: ellipsis;
41943
+ white-space: nowrap;
41944
+ }
41945
+ .config-value.masked { color: #999; }
41946
+ .form-view { display: none; }
41947
+ .form-group { margin-bottom: 16px; }
41948
+ label {
41949
+ display: flex;
41950
+ align-items: center;
41951
+ gap: 6px;
41952
+ margin-bottom: 6px;
41953
+ color: #333;
41954
+ font-weight: 500;
41955
+ font-size: 14px;
41956
+ }
41957
+ input {
41958
+ width: 100%;
41959
+ padding: 10px 12px;
41960
+ border: 2px solid #e1e4e8;
41961
+ border-radius: 8px;
41962
+ font-size: 13px;
41963
+ font-family: "Helvetica Neue LT Pro", "Open Sans", 'Courier New', monospace !important;
41964
+ transition: border-color 0.2s;
41965
+ }
41966
+ input:focus {
41967
+ outline: none;
41968
+ border-color: #b91d8f;
41969
+ }
41970
+ .optional {
41971
+ color: #999;
41972
+ font-weight: normal;
41973
+ font-size: 12px;
41974
+ }
41975
+ .tooltip {
41976
+ position: relative;
41977
+ display: inline-flex;
41978
+ align-items: center;
41979
+ justify-content: center;
41980
+ width: 16px;
41981
+ height: 16px;
41982
+ background: #b91d8f;
41983
+ color: white;
41984
+ border-radius: 50%;
41985
+ font-size: 11px;
41986
+ font-weight: bold;
41987
+ cursor: pointer;
41988
+ flex-shrink: 0;
41989
+ user-select: none;
41990
+ }
41991
+ .tooltip-content {
41992
+ display: none !important;
41993
+ position: fixed;
41994
+ max-width: 380px;
41995
+ width: max-content;
41996
+ background: white;
41997
+ border-radius: 8px;
41998
+ box-shadow: 0 4px 20px rgba(0,0,0,0.3);
41999
+ z-index: 10000;
42000
+ overflow: hidden;
42001
+ border: 2px solid #b91d8f;
42002
+ pointer-events: auto;
42003
+ }
42004
+ .tooltip-content.active {
42005
+ display: block !important;
42006
+ }
42007
+ .tooltip-arrow {
42008
+ position: absolute;
42009
+ width: 0;
42010
+ height: 0;
42011
+ border: 8px solid transparent;
42012
+ z-index: 1;
42013
+ }
42014
+ .tooltip-arrow.left {
42015
+ left: -16px;
42016
+ top: 50%;
42017
+ transform: translateY(-50%);
42018
+ border-right-color: #b91d8f;
42019
+ }
42020
+ .tooltip-arrow.right {
42021
+ right: -16px;
42022
+ top: 50%;
42023
+ transform: translateY(-50%);
42024
+ border-left-color: #b91d8f;
42025
+ }
42026
+ .tooltip-arrow.top {
42027
+ top: -16px;
42028
+ left: 50%;
42029
+ transform: translateX(-50%);
42030
+ border-bottom-color: #b91d8f;
42031
+ }
42032
+ .tooltip-arrow.bottom {
42033
+ bottom: -16px;
42034
+ left: 50%;
42035
+ transform: translateX(-50%);
42036
+ border-top-color: #b91d8f;
42037
+ }
42038
+ .tooltip-header {
42039
+ background: #b91d8f;
42040
+ color: white;
42041
+ padding: 8px 12px;
42042
+ font-weight: 600;
42043
+ font-size: 13px;
42044
+ }
42045
+ .tooltip-body {
42046
+ padding: 10px 12px;
42047
+ }
42048
+ .tooltip-image {
42049
+ width: 100%;
42050
+ max-height: 200px;
42051
+ object-fit: contain;
42052
+ border-radius: 4px;
42053
+ margin-bottom: 6px;
42054
+ border: 1px solid #e1e4e8;
42055
+ background: #f8f9fa;
42056
+ }
42057
+ .tooltip-text {
42058
+ color: #555;
42059
+ font-size: 12px;
42060
+ line-height: 1.4;
42061
+ font-style: italic;
42062
+ }
42063
+ .button-group {
42064
+ display: flex;
42065
+ gap: 10px;
42066
+ margin-top: 20px;
42067
+ }
42068
+ button {
42069
+ flex: 1;
42070
+ padding: 12px;
42071
+ border: 2px solid transparent;
42072
+ border-radius: 8px;
42073
+ font-size: 15px;
42074
+ font-weight: 600;
42075
+ cursor: pointer;
42076
+ transition: all 0.2s;
42077
+ }
42078
+ .btn-primary {
42079
+ background: linear-gradient(135deg, #b91d8f 0%, #7a1460 100%);
42080
+ color: white;
42081
+ }
42082
+ .btn-primary:hover {
42083
+ transform: translateY(-2px);
42084
+ box-shadow: 0 6px 20px rgba(185, 29, 143, 0.4);
42085
+ }
42086
+ .btn-secondary {
42087
+ background: #f6f8fa;
42088
+ color: #666;
42089
+ }
42090
+ .btn-secondary:hover {
42091
+ background: #e1e4e8;
42092
+ }
42093
+ .btn-danger {
42094
+ background: white;
42095
+ color: #b91d8f;
42096
+ border: 2px solid #b91d8f;
42097
+ }
42098
+ .btn-danger:hover {
42099
+ background: #fef1fd;
42100
+ transform: translateY(-1px);
42101
+ }
42102
+ .btn-link {
42103
+ background: transparent;
42104
+ color: #b91d8f;
42105
+ padding: 8px;
42106
+ font-size: 14px;
42107
+ }
42108
+ .btn-link:hover {
42109
+ background: #f6f8fa;
42110
+ }
42111
+ .status {
42112
+ margin-top: 20px;
42113
+ padding: 12px;
42114
+ border-radius: 8px;
42115
+ font-size: 14px;
42116
+ display: none;
42117
+ }
42118
+ .status.success {
42119
+ background: #d4edda;
42120
+ color: #155724;
42121
+ border: 1px solid #c3e6cb;
42122
+ }
42123
+ .status.error {
42124
+ background: #f8d7da;
42125
+ color: #721c24;
42126
+ border: 1px solid #f5c6cb;
42127
+ }
42128
+ .status.info {
42129
+ background: #d1ecf1;
42130
+ color: #0c5460;
42131
+ border: 1px solid #bee5eb;
42132
+ }
42133
+ /* Loading overlay and spinner */
42134
+ .loading-overlay {
42135
+ display: none;
42136
+ position: absolute;
42137
+ top: -32px;
42138
+ left: -32px;
42139
+ right: -32px;
42140
+ bottom: -32px;
42141
+ background: rgba(255, 255, 255, 0.85);
42142
+ backdrop-filter: blur(2px);
42143
+ border-radius: 16px;
42144
+ z-index: 1000;
42145
+ justify-content: center;
42146
+ align-items: center;
42147
+ flex-direction: column;
42148
+ gap: 16px;
42149
+ }
42150
+ .loading-overlay.active {
42151
+ display: flex;
42152
+ }
42153
+ .spinner {
42154
+ width: 48px;
42155
+ height: 48px;
42156
+ border: 4px solid #e1e4e8;
42157
+ border-top-color: #b91d8f;
42158
+ border-radius: 50%;
42159
+ animation: spin 0.8s linear infinite;
42160
+ }
42161
+ @keyframes spin {
42162
+ to { transform: rotate(360deg); }
42163
+ }
42164
+ .loading-message {
42165
+ color: #666;
42166
+ font-size: 14px;
42167
+ font-weight: 500;
42168
+ text-align: center;
42169
+ max-width: 300px;
42170
+ }
42171
+ .form-view {
42172
+ position: relative;
42173
+ }
42174
+ .modal-overlay {
42175
+ display: none;
42176
+ position: fixed;
42177
+ top: 0;
42178
+ left: 0;
42179
+ right: 0;
42180
+ bottom: 0;
42181
+ background: rgba(0, 0, 0, 0.5);
42182
+ z-index: 10001;
42183
+ justify-content: center;
42184
+ align-items: center;
42185
+ }
42186
+ .modal-overlay.active {
42187
+ display: flex;
42188
+ }
42189
+ .modal-content {
42190
+ background: white;
42191
+ border-radius: 12px;
42192
+ padding: 30px;
42193
+ max-width: 400px;
42194
+ width: 90%;
42195
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
42196
+ }
42197
+ .modal-title {
42198
+ font-size: 20px;
42199
+ font-weight: 600;
42200
+ color: #333;
42201
+ margin-bottom: 12px;
42202
+ }
42203
+ .modal-message {
42204
+ font-size: 14px;
42205
+ color: #666;
42206
+ margin-bottom: 24px;
42207
+ line-height: 1.5;
42208
+ }
42209
+ .modal-buttons {
42210
+ display: flex;
42211
+ gap: 10px;
42212
+ justify-content: flex-end;
42213
+ }
42214
+ .modal-buttons button {
42215
+ padding: 10px 20px;
42216
+ font-size: 14px;
42217
+ flex: none;
42218
+ }
42219
+ </style>
42220
+ </head>
42221
+ <body>
42222
+ <div class="container">
42223
+ <div id="quickSigninView" class="quick-signin">
42224
+ <div id="loadingOverlayQuickSignin" class="loading-overlay">
42225
+ <div class="spinner"></div>
42226
+ <div id="loadingMessageQuickSignin" class="loading-message">Redirecting to login...</div>
42227
+ </div>
42228
+ <div class="icon">\uD83D\uDD10</div>
42229
+ <h1>Welcome Back!</h1>
42230
+ <p class="subtitle">Ready to sign in with your saved configuration</p>
42231
+ <div class="saved-config">
42232
+ <div class="saved-config-title">Saved Configuration</div>
42233
+ <div id="savedConfigList"></div>
42234
+ </div>
42235
+ <div class="button-group">
42236
+ <button type="button" class="btn-danger" onclick="clearConfigAndShowForm()">Clear All</button>
42237
+ <button type="button" class="btn-primary" onclick="signInWithSavedConfig()">Sign In</button>
42238
+ </div>
42239
+ <button type="button" class="btn-link" onclick="showForm()" style="width: 100%; margin-top: 10px;">Edit Configuration</button>
42240
+ </div>
42241
+ <div id="formView" class="form-view">
42242
+ <div id="loadingOverlay" class="loading-overlay">
42243
+ <div class="spinner"></div>
42244
+ <div id="loadingMessage" class="loading-message">Saving configuration...</div>
42245
+ </div>
42246
+ <h1>\uD83D\uDD10 eGain MCP Configuration</h1>
42247
+ <p class="subtitle">Enter details from your eGain <strong>Admin Console</strong></p>
42248
+ <form id="configForm">
42249
+ <div class="form-group">
42250
+ <label for="egainUrl">
42251
+ <span>eGain Environment URL</span>
42252
+ <span class="tooltip" onmouseenter="showTooltip(event, 'egainUrl')" onmouseleave="hideTooltip(event, 'egainUrl')">?</span>
42253
+ </label>
42254
+ <input type="text" id="egainUrl" name="egainUrl" placeholder="https://your-environment.egain.cloud" required>
42255
+ </div>
42256
+ <div class="form-group">
42257
+ <label for="authUrl">
42258
+ <span>Authorization URL</span>
42259
+ <span class="tooltip" onmouseenter="showTooltip(event, 'authUrl')" onmouseleave="hideTooltip(event, 'authUrl')">?</span>
42260
+ </label>
42261
+ <input type="text" id="authUrl" name="authUrl" placeholder="https://login.egain.cloud/.../oauth2/authorize" required>
42262
+ </div>
42263
+ <div class="form-group">
42264
+ <label for="accessTokenUrl">
42265
+ <span>Access Token URL</span>
42266
+ <span class="tooltip" onmouseenter="showTooltip(event, 'accessTokenUrl')" onmouseleave="hideTooltip(event, 'accessTokenUrl')">?</span>
42267
+ </label>
42268
+ <input type="text" id="accessTokenUrl" name="accessTokenUrl" placeholder="https://login.egain.cloud/.../oauth2/token" required>
42269
+ </div>
42270
+ <div class="form-group">
42271
+ <label for="clientId">
42272
+ <span>Client ID</span>
42273
+ <span class="tooltip" onmouseenter="showTooltip(event, 'clientId')" onmouseleave="hideTooltip(event, 'clientId')">?</span>
42274
+ </label>
42275
+ <input type="text" id="clientId" name="clientId" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" required>
42276
+ </div>
42277
+ <div class="form-group">
42278
+ <label for="redirectUrl">
42279
+ <span>Redirect URL</span>
42280
+ <span class="tooltip" onmouseenter="showTooltip(event, 'redirectUrl')" onmouseleave="hideTooltip(event, 'redirectUrl')">?</span>
42281
+ </label>
42282
+ <input type="text" id="redirectUrl" name="redirectUrl" placeholder="https://your-redirect-url.com/" required>
42283
+ </div>
42284
+
42285
+ <!-- Advanced Settings Toggle -->
42286
+ <div style="margin: 12px 0;">
42287
+ <button type="button" onclick="toggleAdvancedSettings()" style="padding: 6px 0; font-size: 13px; display: flex; align-items: center; gap: 6px; background: none; border: none; color: #b91d8f; cursor: pointer; font-family: inherit;">
42288
+ <span id="advancedToggleIcon">▶</span>
42289
+ <span>Advanced Settings</span>
42290
+ <span style="color: #999; font-size: 11px;">(optional)</span>
42291
+ </button>
42292
+ </div>
42293
+
42294
+ <!-- Advanced Settings Section (hidden by default) -->
42295
+ <div id="advancedSettings" style="display: none;">
42296
+ <div class="form-group">
42297
+ <label for="clientSecret">
42298
+ <span>Client Secret</span>
42299
+ <span class="tooltip" onmouseenter="showTooltip(event, 'clientSecret')" onmouseleave="hideTooltip(event, 'clientSecret')">?</span>
42300
+ <span class="optional">(optional)</span>
42301
+ </label>
42302
+ <input type="password" id="clientSecret" name="clientSecret" placeholder="Required for normal authentication flow/non-PKCE">
42303
+ </div>
42304
+ <div class="form-group">
42305
+ <label for="scopePrefix">
42306
+ <span>Scope Prefix</span>
42307
+ <span class="tooltip" onmouseenter="showTooltip(event, 'scopePrefix')" onmouseleave="hideTooltip(event, 'scopePrefix')">?</span>
42308
+ <span class="optional">(optional)</span>
42309
+ </label>
42310
+ <input type="text" id="scopePrefix" name="scopePrefix" placeholder="https://your.scope-prefix.cloud/auth/">
42311
+ </div>
42312
+ </div>
42313
+ <div style="font-size: 11px; color: #999; margin: 12px 0 0 0; line-height: 1.4;">
42314
+ \uD83D\uDD12 Your configuration will be securely saved to your <code style="background: #f0f0f0; padding: 2px 4px; border-radius: 3px; font-size: 10px;">~/.egain-mcp/config.json</code>.
42315
+ </div>
42316
+ <div class="button-group">
42317
+ <button type="button" class="btn-secondary" onclick="cancelForm()">Cancel</button>
42318
+ <button type="submit" class="btn-primary">Save & Authenticate</button>
42319
+ </div>
42320
+ </form>
42321
+ </div>
42322
+ <div id="status" class="status"></div>
42323
+ </div>
42324
+
42325
+ <!-- Confirmation Modal -->
42326
+ <div id="confirmModal" class="modal-overlay" onclick="if(event.target === this) closeModal(false)">
42327
+ <div class="modal-content">
42328
+ <div class="modal-title" id="modalTitle">Confirm Action</div>
42329
+ <div class="modal-message" id="modalMessage">Are you sure?</div>
42330
+ <div class="modal-buttons">
42331
+ <button type="button" class="btn-secondary" onclick="closeModal(false)">Cancel</button>
42332
+ <button type="button" class="btn-danger" id="modalConfirmBtn" onclick="closeModal(true)">Confirm</button>
42333
+ </div>
42334
+ </div>
42335
+ </div>
42336
+
42337
+ <!-- Tooltip Popups (outside container for proper fixed positioning) -->
42338
+ <div id="tooltip-egainUrl" class="tooltip-content">
42339
+ <div class="tooltip-header">eGain Environment URL</div>
42340
+ <div class="tooltip-body">
42341
+ <img src="/img/env-tooltip.png" class="tooltip-image" alt="eGain Environment URL" onerror="this.style.display='none'">
42342
+ <div class="tooltip-text">Enter the domain URL displayed in your browser when accessing the eGain application.</div>
42343
+ </div>
42344
+ </div>
42345
+
42346
+ <div id="tooltip-authUrl" class="tooltip-content">
42347
+ <div class="tooltip-header">Authorization URL</div>
42348
+ <div class="tooltip-body">
42349
+ <img src="/img/authurl-tooltip.png" class="tooltip-image" alt="Authorization URL" onerror="this.style.display='none'">
42350
+ <div class="tooltip-text">In the Partition space, go to Integration → Client Application → Metadata, and copy the Authorization URL.</div>
42351
+ </div>
42352
+ </div>
42353
+
42354
+ <div id="tooltip-accessTokenUrl" class="tooltip-content">
42355
+ <div class="tooltip-header">Access Token URL</div>
42356
+ <div class="tooltip-body">
42357
+ <img src="/img/accesstoken-tooltip.png" class="tooltip-image" alt="Access Token URL" onerror="this.style.display='none'">
42358
+ <div class="tooltip-text">In the Partition space, go to Integration → Client Application → Metadata, and copy the Access Token URL.</div>
42359
+ </div>
42360
+ </div>
42361
+
42362
+ <div id="tooltip-clientId" class="tooltip-content">
42363
+ <div class="tooltip-header">Client ID</div>
42364
+ <div class="tooltip-body">
42365
+ <img src="/img/clientid-tooltip.png" class="tooltip-image" alt="Client ID" onerror="this.style.display='none'">
42366
+ <div class="tooltip-text">In the Partition space, go to Integration → Client Application, select your client app, and copy the Client ID.</div>
42367
+ </div>
42368
+ </div>
42369
+
42370
+ <div id="tooltip-redirectUrl" class="tooltip-content">
42371
+ <div class="tooltip-header">Redirect URL</div>
42372
+ <div class="tooltip-body">
42373
+ <img src="/img/redirect-tooltip.png" class="tooltip-image" alt="Redirect URL" onerror="this.style.display='none'">
42374
+ <div class="tooltip-text">In the Partition space, go to Integration → Client Application, select your client app, and copy the Redirect URL.</div>
42375
+ </div>
42376
+ </div>
42377
+
42378
+ <div id="tooltip-clientSecret" class="tooltip-content">
42379
+ <div class="tooltip-header">Client Secret</div>
42380
+ <div class="tooltip-body">
42381
+ <img src="/img/clientsecret-tooltip.png" class="tooltip-image" alt="Client Secret" onerror="this.style.display='none'">
42382
+ <div class="tooltip-text">In the Partition space, go to Integration → Client Application, select your client app, and copy the Client Secret under Secrets.</div>
42383
+ </div>
42384
+ </div>
42385
+
42386
+ <div id="tooltip-scopePrefix" class="tooltip-content">
42387
+ <div class="tooltip-header">Scope Prefix</div>
42388
+ <div class="tooltip-body">
42389
+ <img src="/img/scopeprefix-tooltip.png" class="tooltip-image" alt="Scope Prefix" onerror="this.style.display='none'">
42390
+ <div class="tooltip-text">In the Partition space, go to Integration → Client Application → Metadata, and copy the API Permission Prefix.</div>
42391
+ </div>
42392
+ </div>
42393
+
42394
+ <script src="/config-page.js"></script>
42395
+ </body>
42396
+ </html>`, CONFIG_PAGE_JS = `const FIELDS = ['egainUrl', 'authUrl', 'accessTokenUrl', 'clientId', 'redirectUrl', 'clientSecret', 'scopePrefix'];
42397
+ const FIELD_LABELS = {
42398
+ egainUrl: 'eGain URL', authUrl: 'Auth URL', accessTokenUrl: 'Token URL',
42399
+ clientId: 'Client ID', redirectUrl: 'Redirect URL', clientSecret: 'Client Secret',
42400
+ scopePrefix: 'Scope Prefix'
42401
+ };
42402
+
42403
+ let savedConfigData = null; // Store config in memory only
42404
+ let authenticationStarted = false; // Track if user started authentication process
42405
+ let lastSubmittedConfig = null; // Track last submitted config to prevent duplicate submissions
42406
+ let isSubmitting = false; // Track if form submission is in progress
42407
+
42408
+ // Fetch saved config from backend (secure file storage)
42409
+ async function loadSavedConfig() {
42410
+ try {
42411
+ const response = await fetch('/get-config');
42412
+ if (response.ok) {
42413
+ const data = await response.json();
42414
+ if (data.config && data.config.egainUrl && data.config.clientId) {
42415
+ // Only set if we have valid required fields
42416
+ savedConfigData = data.config;
42417
+ return true;
42418
+ }
42419
+ }
42420
+ } catch (error) {
42421
+ console.error('Could not load saved config:', error);
42422
+ }
42423
+ savedConfigData = null;
42424
+ return false;
42425
+ }
42426
+
42427
+ function hasSavedConfig() {
42428
+ return savedConfigData &&
42429
+ savedConfigData.egainUrl &&
42430
+ savedConfigData.clientId &&
42431
+ savedConfigData.authUrl &&
42432
+ savedConfigData.accessTokenUrl;
42433
+ }
42434
+
42435
+ function displaySavedConfig() {
42436
+ const listEl = document.getElementById('savedConfigList');
42437
+ listEl.innerHTML = '';
42438
+ if (!savedConfigData) return;
42439
+
42440
+ FIELDS.forEach(field => {
42441
+ const value = savedConfigData[field];
42442
+ if (value) {
42443
+ const item = document.createElement('div');
42444
+ item.className = 'config-item';
42445
+ const label = document.createElement('span');
42446
+ label.className = 'config-label';
42447
+ label.textContent = FIELD_LABELS[field];
42448
+ const valueSpan = document.createElement('span');
42449
+ valueSpan.className = 'config-value';
42450
+ if (field === 'clientSecret') {
42451
+ valueSpan.classList.add('masked');
42452
+ valueSpan.textContent = '••••••••';
42453
+ } else if (field === 'clientId') {
42454
+ valueSpan.textContent = value.substring(0, 8) + '...';
42455
+ } else {
42456
+ valueSpan.textContent = value.length > 40 ? value.substring(0, 40) + '...' : value;
42457
+ }
42458
+ item.appendChild(label);
42459
+ item.appendChild(valueSpan);
42460
+ listEl.appendChild(item);
42461
+ }
42462
+ });
42463
+ }
42464
+
42465
+ function showQuickSignin() {
42466
+ document.getElementById('quickSigninView').style.display = 'block';
42467
+ document.getElementById('formView').style.display = 'none';
42468
+ displaySavedConfig();
42469
+ }
42470
+
42471
+ function showForm() {
42472
+ document.getElementById('quickSigninView').style.display = 'none';
42473
+ document.getElementById('formView').style.display = 'block';
42474
+ loadFormValues();
42475
+ }
42476
+
42477
+ function loadFormValues() {
42478
+ if (!savedConfigData) return;
42479
+
42480
+ // Load all field values
42481
+ FIELDS.forEach(field => {
42482
+ const value = savedConfigData[field];
42483
+ if (value) document.getElementById(field).value = value;
42484
+ });
42485
+
42486
+ // Show advanced settings if clientSecret or scopePrefix exist
42487
+ if (savedConfigData.clientSecret || savedConfigData.scopePrefix) {
42488
+ toggleAdvancedSettings();
42489
+ }
42490
+ }
42491
+
42492
+ async function clearConfigAndShowForm() {
42493
+ showModal(
42494
+ 'Clear All Configuration?',
42495
+ 'This will delete all saved OAuth settings from your home directory. You will need to re-enter them next time.',
42496
+ async (confirmed) => {
42497
+ if (confirmed) {
42498
+ try {
42499
+ const response = await fetch('/clear-config', { method: 'POST' });
42500
+ if (response.ok) {
42501
+ savedConfigData = null;
42502
+ FIELDS.forEach(field => {
42503
+ document.getElementById(field).value = '';
42504
+ });
42505
+ showStatus('Configuration cleared successfully', 'success');
42506
+ showForm();
42507
+ } else {
42508
+ showStatus('Failed to clear configuration', 'error');
42509
+ }
42510
+ } catch (error) {
42511
+ showStatus('Error clearing configuration: ' + error.message, 'error');
42512
+ }
42513
+ }
42514
+ },
42515
+ 'Clear All'
42516
+ );
42517
+ }
42518
+
42519
+ function cancelForm() {
42520
+ if (hasSavedConfig()) {
42521
+ showQuickSignin();
42522
+ } else {
42523
+ showModal(
42524
+ 'Cancel Authentication?',
42525
+ "You haven't saved any configuration yet. This will cancel the authentication process.",
42526
+ async (confirmed) => {
42527
+ if (confirmed) {
42528
+ // Notify server that user cancelled
42529
+ try {
42530
+ await fetch('/cancel', { method: 'POST' });
42531
+ } catch (error) {
42532
+ console.error('Could not notify server of cancellation:', error);
42533
+ }
42534
+ window.close();
42535
+ }
42536
+ },
42537
+ 'Cancel'
42538
+ );
42539
+ }
42540
+ }
42541
+
42542
+ async function signInWithSavedConfig() {
42543
+ authenticationStarted = true; // Mark that auth has started
42544
+
42545
+ // Show loading overlay
42546
+ showLoadingOverlay('Redirecting to login...');
42547
+
42548
+ try {
42549
+ const response = await fetch('/get-oauth-url', { method: 'POST' });
42550
+ const result = await response.json();
42551
+
42552
+ if (response.ok && result.oauthUrl) {
42553
+ console.log('\uD83D\uDD17 OAuth URL:', result.oauthUrl);
42554
+ // Redirect after a brief delay to show loading state
42555
+ setTimeout(() => {
42556
+ window.location.href = result.oauthUrl;
42557
+ }, 500);
42558
+ } else {
42559
+ hideLoadingOverlay();
42560
+ showStatus('❌ ' + (result.error || 'Failed to get OAuth URL'), 'error');
42561
+ authenticationStarted = false; // Reset on error
42562
+ }
42563
+ } catch (error) {
42564
+ hideLoadingOverlay();
42565
+ showStatus('❌ Error: ' + error.message, 'error');
42566
+ authenticationStarted = false; // Reset on error
42567
+ }
42568
+ }
42569
+
42570
+ function showStatus(message, type) {
42571
+ const statusEl = document.getElementById('status');
42572
+ statusEl.textContent = message;
42573
+ statusEl.className = 'status ' + type;
42574
+ statusEl.style.display = 'block';
42575
+ // Auto-hide info and success messages after a delay
42576
+ if (type === 'info') {
42577
+ setTimeout(() => {
42578
+ // Only hide if it's still an info message (not changed to success/error)
42579
+ if (statusEl.className === 'status info') {
42580
+ statusEl.style.display = 'none';
42581
+ }
42582
+ }, 3000);
42583
+ } else if (type === 'success') {
42584
+ setTimeout(() => {
42585
+ // Only hide if it's still a success message (not changed to error)
42586
+ if (statusEl.className === 'status success') {
42587
+ statusEl.style.display = 'none';
42588
+ }
42589
+ }, 4000);
42590
+ }
42591
+ }
42592
+
42593
+ function configValuesEqual(config1, config2) {
42594
+ // Compare all fields that matter
42595
+ const fieldsToCompare = ['egainUrl', 'authUrl', 'accessTokenUrl', 'clientId', 'redirectUrl', 'clientSecret', 'scopePrefix'];
42596
+ for (const field of fieldsToCompare) {
42597
+ const val1 = (config1[field] || '').trim();
42598
+ const val2 = (config2[field] || '').trim();
42599
+ if (val1 !== val2) {
42600
+ return false;
42601
+ }
42602
+ }
42603
+ return true;
42604
+ }
42605
+
42606
+ function showLoadingOverlay(message) {
42607
+ // Show overlay for form view
42608
+ const overlay = document.getElementById('loadingOverlay');
42609
+ const loadingMessage = document.getElementById('loadingMessage');
42610
+ if (overlay) {
42611
+ overlay.classList.add('active');
42612
+ if (loadingMessage) {
42613
+ loadingMessage.textContent = message || 'Saving configuration...';
42614
+ }
42615
+ }
42616
+
42617
+ // Show overlay for quick signin view
42618
+ const overlayQuickSignin = document.getElementById('loadingOverlayQuickSignin');
42619
+ const loadingMessageQuickSignin = document.getElementById('loadingMessageQuickSignin');
42620
+ if (overlayQuickSignin) {
42621
+ overlayQuickSignin.classList.add('active');
42622
+ if (loadingMessageQuickSignin) {
42623
+ loadingMessageQuickSignin.textContent = message || 'Redirecting to login...';
42624
+ }
42625
+ }
42626
+ }
42627
+
42628
+ function hideLoadingOverlay() {
42629
+ // Hide overlay for form view
42630
+ const overlay = document.getElementById('loadingOverlay');
42631
+ if (overlay) {
42632
+ overlay.classList.remove('active');
42633
+ }
42634
+
42635
+ // Hide overlay for quick signin view
42636
+ const overlayQuickSignin = document.getElementById('loadingOverlayQuickSignin');
42637
+ if (overlayQuickSignin) {
42638
+ overlayQuickSignin.classList.remove('active');
42639
+ }
42640
+ }
42641
+
42642
+ async function authenticateWithConfig(config) {
42643
+ // Prevent duplicate submissions
42644
+ if (isSubmitting) {
42645
+ return; // Already submitting, ignore
42646
+ }
42647
+
42648
+ // Check if values have changed since last submission
42649
+ if (lastSubmittedConfig && configValuesEqual(config, lastSubmittedConfig)) {
42650
+ showStatus('⚠️ Configuration unchanged. Already saved.', 'info');
42651
+ return; // Values haven't changed, don't submit again
42652
+ }
42653
+
42654
+ try {
42655
+ isSubmitting = true; // Mark as submitting
42656
+ const submitButton = document.querySelector('button[type="submit"]');
42657
+ if (submitButton) {
42658
+ submitButton.disabled = true;
42659
+ submitButton.textContent = 'Saving...';
42660
+ }
42661
+
42662
+ // Show loading overlay with spinner
42663
+ showLoadingOverlay('Saving configuration...');
42664
+ authenticationStarted = true; // Mark that auth has started
42665
+
42666
+ const response = await fetch('/authenticate', {
42667
+ method: 'POST',
42668
+ headers: { 'Content-Type': 'application/json' },
42669
+ body: JSON.stringify(config)
42670
+ });
42671
+ const result = await response.json();
42672
+
42673
+ if (response.ok && result.oauthUrl) {
42674
+ // Config saved! Store this config as last submitted
42675
+ lastSubmittedConfig = { ...config };
42676
+
42677
+ console.log('\uD83D\uDD17 OAuth URL:', result.oauthUrl);
42678
+
42679
+ // Update loading message (overlay already shows the status, no need for status message)
42680
+ showLoadingOverlay('Configuration saved! Redirecting to login...');
42681
+
42682
+ setTimeout(() => {
42683
+ window.location.href = result.oauthUrl;
42684
+ }, 500);
42685
+ } else if (response.ok && result.success) {
42686
+ lastSubmittedConfig = { ...config };
42687
+ hideLoadingOverlay();
42688
+ showStatus('✅ ' + result.message, 'success');
42689
+ setTimeout(() => { window.close(); }, 2000);
42690
+ } else {
42691
+ hideLoadingOverlay();
42692
+ showStatus('❌ ' + (result.error || 'Authentication failed'), 'error');
42693
+ authenticationStarted = false; // Reset on error
42694
+ isSubmitting = false; // Reset submitting flag
42695
+ if (submitButton) {
42696
+ submitButton.disabled = false;
42697
+ submitButton.textContent = 'Save & Authenticate';
42698
+ }
42699
+ }
42700
+ } catch (error) {
42701
+ hideLoadingOverlay();
42702
+ showStatus('❌ Error: ' + error.message, 'error');
42703
+ authenticationStarted = false; // Reset on error
42704
+ isSubmitting = false; // Reset submitting flag
42705
+ const submitButton = document.querySelector('button[type="submit"]');
42706
+ if (submitButton) {
42707
+ submitButton.disabled = false;
42708
+ submitButton.textContent = 'Save & Authenticate';
42709
+ }
42710
+ }
42711
+ }
42712
+
42713
+ document.getElementById('configForm').addEventListener('submit', async (e) => {
42714
+ e.preventDefault();
42715
+
42716
+ // Prevent duplicate submissions
42717
+ if (isSubmitting) {
42718
+ return;
42719
+ }
42720
+
42721
+ const formData = new FormData(e.target);
42722
+ const config = {};
42723
+ for (let [key, value] of formData.entries()) {
42724
+ config[key] = value;
42725
+ }
42726
+
42727
+ // Config is now sent to backend for secure file storage (not cookies)
42728
+ await authenticateWithConfig(config);
42729
+ });
42730
+
42731
+ // Initialize: Load saved config from backend
42732
+ (async () => {
42733
+ const hasConfig = await loadSavedConfig();
42734
+ if (hasConfig) {
42735
+ showQuickSignin();
42736
+ } else {
42737
+ showForm();
42738
+ }
42739
+ })();
42740
+
42741
+ // Custom modal functions
42742
+ let modalCallback = null;
42743
+
42744
+ function showModal(title, message, callback, confirmText = 'Confirm') {
42745
+ document.getElementById('modalTitle').textContent = title;
42746
+ document.getElementById('modalMessage').textContent = message;
42747
+ document.getElementById('modalConfirmBtn').textContent = confirmText;
42748
+ modalCallback = callback;
42749
+ document.getElementById('confirmModal').classList.add('active');
42750
+ }
42751
+
42752
+ function closeModal(confirmed) {
42753
+ document.getElementById('confirmModal').classList.remove('active');
42754
+ if (modalCallback) {
42755
+ modalCallback(confirmed);
42756
+ modalCallback = null;
42757
+ }
42758
+ }
42759
+
42760
+ // Advanced settings toggle
42761
+ function toggleAdvancedSettings() {
42762
+ const advancedSection = document.getElementById('advancedSettings');
42763
+ const toggleIcon = document.getElementById('advancedToggleIcon');
42764
+
42765
+ if (advancedSection.style.display === 'none') {
42766
+ advancedSection.style.display = 'block';
42767
+ toggleIcon.textContent = '▼';
42768
+ } else {
42769
+ advancedSection.style.display = 'none';
42770
+ toggleIcon.textContent = '▶';
42771
+ }
42772
+ }
42773
+
42774
+ // Tooltip functions
42775
+ function showTooltip(event, fieldName) {
42776
+ event.preventDefault(); // Prevent label from focusing input
42777
+ event.stopPropagation(); // Prevent event from bubbling
42778
+
42779
+ const tooltipId = 'tooltip-' + fieldName;
42780
+ let tooltipElement = document.getElementById(tooltipId);
42781
+ const button = event.currentTarget;
42782
+
42783
+ if (tooltipElement) {
42784
+ // Show the tooltip first to get its dimensions
42785
+ tooltipElement.classList.add('active');
42786
+
42787
+ // Get actual dimensions after showing
42788
+ const buttonRect = button.getBoundingClientRect();
42789
+ const tooltipRect = tooltipElement.getBoundingClientRect();
42790
+ const tooltipWidth = tooltipRect.width || 380;
42791
+ const tooltipHeight = tooltipRect.height || 300;
42792
+
42793
+ const spacing = 16; // Space between button and tooltip
42794
+ const viewportPadding = 10; // Padding from viewport edges
42795
+ const horizontalComfortZone = 50; // Extra space needed to avoid cramped horizontal positioning
42796
+
42797
+ // Calculate available space on all sides
42798
+ const spaceOnRight = window.innerWidth - buttonRect.right;
42799
+ const spaceOnLeft = buttonRect.left;
42800
+ const spaceBelow = window.innerHeight - buttonRect.bottom;
42801
+ const spaceAbove = buttonRect.top;
42802
+
42803
+ let left, top;
42804
+ let arrowSide = 'left'; // Default: tooltip on right, arrow on left
42805
+
42806
+ // Check if horizontal positioning would be too cramped (tooltip might cover the icon)
42807
+ const horizontalSpaceTight = (spaceOnRight < tooltipWidth + horizontalComfortZone) &&
42808
+ (spaceOnLeft < tooltipWidth + horizontalComfortZone);
42809
+
42810
+ if (horizontalSpaceTight) {
42811
+ // Use vertical positioning to avoid covering the icon
42812
+ // Determine if we're in the top or bottom half of the viewport
42813
+ const inTopHalf = buttonRect.top < window.innerHeight / 2;
42814
+
42815
+ if (inTopHalf && spaceBelow >= tooltipHeight + spacing + viewportPadding) {
42816
+ // Position below
42817
+ left = buttonRect.left + (buttonRect.width / 2) - (tooltipWidth / 2);
42818
+ top = buttonRect.bottom + spacing;
42819
+ arrowSide = 'top';
42820
+ } else if (!inTopHalf && spaceAbove >= tooltipHeight + spacing + viewportPadding) {
42821
+ // Position above
42822
+ left = buttonRect.left + (buttonRect.width / 2) - (tooltipWidth / 2);
42823
+ top = buttonRect.top - tooltipHeight - spacing;
42824
+ arrowSide = 'bottom';
42825
+ } else if (spaceBelow > spaceAbove) {
42826
+ // Not enough vertical space either, prefer below
42827
+ left = buttonRect.left + (buttonRect.width / 2) - (tooltipWidth / 2);
42828
+ top = buttonRect.bottom + spacing;
42829
+ arrowSide = 'top';
42830
+ } else {
42831
+ // Prefer above
42832
+ left = buttonRect.left + (buttonRect.width / 2) - (tooltipWidth / 2);
42833
+ top = buttonRect.top - tooltipHeight - spacing;
42834
+ arrowSide = 'bottom';
42835
+ }
42836
+
42837
+ // Keep horizontally centered within viewport
42838
+ if (left < viewportPadding) {
42839
+ left = viewportPadding;
42840
+ }
42841
+ if (left + tooltipWidth > window.innerWidth - viewportPadding) {
42842
+ left = window.innerWidth - tooltipWidth - viewportPadding;
42843
+ }
42844
+
42845
+ } else {
42846
+ // Use horizontal positioning (original logic)
42847
+ // Prefer right side if there's enough space
42848
+ if (spaceOnRight >= tooltipWidth + spacing + viewportPadding) {
42849
+ // Position to the right
42850
+ left = buttonRect.right + spacing;
42851
+ arrowSide = 'left';
42852
+ } else if (spaceOnLeft >= tooltipWidth + spacing + viewportPadding) {
42853
+ // Position to the left
42854
+ left = buttonRect.left - tooltipWidth - spacing;
42855
+ arrowSide = 'right';
42856
+ } else {
42857
+ // Not enough space on either side, use the side with more space
42858
+ if (spaceOnRight > spaceOnLeft) {
42859
+ left = buttonRect.right + spacing;
42860
+ arrowSide = 'left';
42861
+ // Allow tooltip to go to edge of screen
42862
+ if (left + tooltipWidth > window.innerWidth - viewportPadding) {
42863
+ left = window.innerWidth - tooltipWidth - viewportPadding;
42864
+ }
42865
+ } else {
42866
+ left = buttonRect.left - tooltipWidth - spacing;
42867
+ arrowSide = 'right';
42868
+ // Allow tooltip to go to edge of screen
42869
+ if (left < viewportPadding) {
42870
+ left = viewportPadding;
42871
+ }
42872
+ }
42873
+ }
42874
+
42875
+ // Center vertically relative to button
42876
+ top = buttonRect.top + (buttonRect.height / 2) - (tooltipHeight / 2);
42877
+
42878
+ // Keep tooltip within viewport vertically
42879
+ if (top < viewportPadding) {
42880
+ top = viewportPadding;
42881
+ }
42882
+ if (top + tooltipHeight > window.innerHeight - viewportPadding) {
42883
+ top = window.innerHeight - tooltipHeight - viewportPadding;
42884
+ }
42885
+ }
42886
+
42887
+ // Apply positioning
42888
+ tooltipElement.style.left = left + 'px';
42889
+ tooltipElement.style.top = top + 'px';
42890
+
42891
+ // Update arrow direction
42892
+ const arrow = tooltipElement.querySelector('.tooltip-arrow');
42893
+ if (arrow) {
42894
+ arrow.className = 'tooltip-arrow ' + arrowSide;
42895
+ }
42896
+ }
42897
+ }
42898
+
42899
+ function hideTooltip(event, fieldName) {
42900
+ const tooltipId = 'tooltip-' + fieldName;
42901
+ let tooltipElement = document.getElementById(tooltipId);
42902
+
42903
+ if (tooltipElement) {
42904
+ tooltipElement.classList.remove('active');
42905
+ }
42906
+ }
42907
+
42908
+ // Close tooltip on ESC key
42909
+ document.addEventListener('keydown', function(e) {
42910
+ if (e.key === 'Escape') {
42911
+ document.querySelectorAll('.tooltip-content').forEach(tip => {
42912
+ tip.classList.remove('active');
42913
+ });
42914
+ }
42915
+ });
42916
+
42917
+ // Notify server if window is closed without starting authentication
42918
+ window.addEventListener('beforeunload', function(e) {
42919
+ // Only send cancel if user never started the authentication process
42920
+ // (If they started auth, they either completed it or clicked cancel explicitly)
42921
+ if (!authenticationStarted) {
42922
+ // Use sendBeacon for reliable delivery even as page unloads
42923
+ navigator.sendBeacon('/cancel', '');
42924
+ }
42925
+ });`, SAFARI_WARNING_HTML = `<!DOCTYPE html>
42926
+ <html lang="en">
42927
+ <head>
42928
+ <meta charset="UTF-8">
42929
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
42930
+ <title>Safari Not Supported - eGain MCP</title>
42931
+ <style>
42932
+ * {
42933
+ margin: 0;
42934
+ padding: 0;
42935
+ box-sizing: border-box;
42936
+ }
42937
+
42938
+ body {
42939
+ font-family: "Open Sans", "Segoe UI", "SegoeUI", "Helvetica Neue", Helvetica, Arial, sans-serif !important;
42940
+ background: #fef1fd;
42941
+ min-height: 100vh;
42942
+ display: flex;
42943
+ justify-content: center;
42944
+ align-items: center;
42945
+ padding: 20px;
42946
+ }
42947
+
42948
+ .container {
42949
+ background: white;
42950
+ border-radius: 16px;
42951
+ box-shadow: 0px 0px 30px 0px rgba(0, 0, 0, 0.12);
42952
+ max-width: 500px;
42953
+ width: 100%;
42954
+ padding: 32px;
42955
+ text-align: center;
42956
+ }
42957
+
42958
+ .warning-icon {
42959
+ font-size: 64px;
42960
+ margin-bottom: 20px;
42961
+ }
42962
+
42963
+ h1 {
42964
+ color: #e74c3c;
42965
+ font-size: 28px;
42966
+ margin-bottom: 30px;
42967
+ font-weight: 600;
42968
+ }
42969
+
42970
+ .reason {
42971
+ background: #fff0f6;
42972
+ border-left: 4px solid #d946a6;
42973
+ padding: 20px;
42974
+ text-align: left;
42975
+ border-radius: 4px;
42976
+ }
42977
+
42978
+ .reason strong {
42979
+ color: #a21361;
42980
+ display: block;
42981
+ margin-bottom: 12px;
42982
+ font-size: 16px;
42983
+ }
42984
+
42985
+ .reason p {
42986
+ color: #a21361;
42987
+ font-size: 14px;
42988
+ line-height: 1.6;
42989
+ }
42990
+ </style>
42991
+ </head>
42992
+ <body>
42993
+ <div class="container">
42994
+ <div class="warning-icon">⚠️</div>
42995
+ <h1>Safari Not Supported</h1>
42996
+
42997
+ <div class="reason">
42998
+ <strong>Why?</strong>
42999
+ <p>
43000
+ Safari doesn't support private browsing mode via command line, which is required
43001
+ to protect your OAuth credentials from being cached or leaked. We prioritize your
43002
+ security over convenience.
43003
+ </p>
43004
+ </div>
43005
+ </div>
43006
+ </body>
43007
+ </html>`;
41871
43008
  var init_auth_hook = __esm(() => {
41872
43009
  __filename2 = fileURLToPath(import.meta.url);
41873
43010
  __dirname2 = path.dirname(__filename2);
@@ -42537,9 +43674,130 @@ class ServerRoutingHook {
42537
43674
  }
42538
43675
  }
42539
43676
 
43677
+ // src/hooks/version-check-hook.ts
43678
+ import * as fs3 from "fs";
43679
+ import * as path3 from "path";
43680
+ import { fileURLToPath as fileURLToPath3 } from "url";
43681
+ function compareVersions(v1, v2) {
43682
+ const parts1 = v1.split(".").map(Number);
43683
+ const parts2 = v2.split(".").map(Number);
43684
+ for (let i = 0;i < Math.max(parts1.length, parts2.length); i++) {
43685
+ const part1 = parts1[i] || 0;
43686
+ const part2 = parts2[i] || 0;
43687
+ if (part1 > part2)
43688
+ return 1;
43689
+ if (part1 < part2)
43690
+ return -1;
43691
+ }
43692
+ return 0;
43693
+ }
43694
+ function isGitRepo(projectRoot) {
43695
+ try {
43696
+ const gitDir = path3.join(projectRoot, ".git");
43697
+ return fs3.existsSync(gitDir) || fs3.existsSync(path3.join(projectRoot, ".git", "HEAD"));
43698
+ } catch {
43699
+ return false;
43700
+ }
43701
+ }
43702
+ function getLocalVersion() {
43703
+ try {
43704
+ const projectRoot = getProjectRoot3();
43705
+ const packageJsonPath = path3.join(projectRoot, "package.json");
43706
+ if (fs3.existsSync(packageJsonPath)) {
43707
+ const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf8"));
43708
+ return packageJson.version || null;
43709
+ }
43710
+ } catch (error) {
43711
+ console.error("⚠️ Could not read local version:", error);
43712
+ }
43713
+ return null;
43714
+ }
43715
+ async function getLatestVersion() {
43716
+ try {
43717
+ const packageName = "@egain/egain-mcp-server";
43718
+ const registryUrl = `https://registry.npmjs.org/${packageName}`;
43719
+ const response = await fetch(registryUrl, {
43720
+ headers: {
43721
+ Accept: "application/json"
43722
+ }
43723
+ });
43724
+ if (!response.ok) {
43725
+ throw new Error(`npm registry returned ${response.status}`);
43726
+ }
43727
+ const data = await response.json();
43728
+ return data["dist-tags"]?.latest || null;
43729
+ } catch (error) {
43730
+ return null;
43731
+ }
43732
+ }
43733
+
43734
+ class VersionCheckHook {
43735
+ checkPerformed = false;
43736
+ sdkInit(opts) {
43737
+ if (this.checkPerformed) {
43738
+ return opts;
43739
+ }
43740
+ this.checkPerformed = true;
43741
+ setImmediate(async () => {
43742
+ try {
43743
+ const localVersion = getLocalVersion();
43744
+ if (!localVersion) {
43745
+ console.error("⚠️ Could not determine local version");
43746
+ return;
43747
+ }
43748
+ console.error(`\uD83D\uDCE6 Checking for updates (current version: ${localVersion})...`);
43749
+ const latestVersion = await getLatestVersion();
43750
+ if (!latestVersion) {
43751
+ return;
43752
+ }
43753
+ if (compareVersions(latestVersion, localVersion) > 0) {
43754
+ const projectRoot = getProjectRoot3();
43755
+ const isGit = isGitRepo(projectRoot);
43756
+ console.error("");
43757
+ console.error("⚠️ UPDATE AVAILABLE");
43758
+ console.error(` Current version: ${localVersion}`);
43759
+ console.error(` Latest version: ${latestVersion}`);
43760
+ if (isGit) {
43761
+ console.error(` Update with: git pull && npm run build`);
43762
+ console.error(` Or visit: https://github.com/eGain/egain-mcp-server`);
43763
+ } else {
43764
+ console.error(` Update with: npm install -g @egain/egain-mcp-server@latest`);
43765
+ console.error(` Or visit: https://www.npmjs.com/package/@egain/egain-mcp-server`);
43766
+ }
43767
+ console.error("");
43768
+ } else {
43769
+ console.error(`✅ You are running the latest version (${localVersion})`);
43770
+ }
43771
+ } catch (error) {
43772
+ if (error instanceof Error && !error.message.includes("fetch")) {
43773
+ console.error("⚠️ Version check failed:", error.message);
43774
+ }
43775
+ }
43776
+ });
43777
+ return opts;
43778
+ }
43779
+ }
43780
+ var __filename4, __dirname4, getProjectRoot3 = () => {
43781
+ let currentDir = __dirname4;
43782
+ while (currentDir !== path3.dirname(currentDir)) {
43783
+ if (fs3.existsSync(path3.join(currentDir, "package.json"))) {
43784
+ return currentDir;
43785
+ }
43786
+ currentDir = path3.dirname(currentDir);
43787
+ }
43788
+ return process.cwd();
43789
+ };
43790
+ var init_version_check_hook = __esm(() => {
43791
+ __filename4 = fileURLToPath3(import.meta.url);
43792
+ __dirname4 = path3.dirname(__filename4);
43793
+ });
43794
+
42540
43795
  // src/hooks/registration.ts
42541
43796
  function initHooks(hooks) {
42542
43797
  console.error("\uD83D\uDE80 Initializing eGain MCP hooks...");
43798
+ const versionCheckHook = new VersionCheckHook;
43799
+ hooks.registerSDKInitHook(versionCheckHook);
43800
+ console.error("✅ VERSION: Registered version check hook for SDK init");
42543
43801
  const serverRoutingHook = new ServerRoutingHook;
42544
43802
  hooks.registerBeforeCreateRequestHook(serverRoutingHook);
42545
43803
  console.error("✅ ROUTING: Registered server routing hook for before create request");
@@ -42559,6 +43817,7 @@ function initHooks(hooks) {
42559
43817
  var init_registration = __esm(() => {
42560
43818
  init_auth_hook();
42561
43819
  init_portal_cache_hook();
43820
+ init_version_check_hook();
42562
43821
  });
42563
43822
 
42564
43823
  // src/hooks/hooks.ts
@@ -43282,14 +44541,14 @@ class ClientSDK {
43282
44541
  }
43283
44542
  }
43284
44543
  _createRequest(context, conf, options) {
43285
- const { method, path: path3, query, headers: opHeaders, security } = conf;
44544
+ const { method, path: path4, query, headers: opHeaders, security } = conf;
43286
44545
  const base = conf.baseURL ?? this._baseURL;
43287
44546
  if (!base) {
43288
44547
  return ERR(new InvalidRequestError("No base URL provided for operation"));
43289
44548
  }
43290
44549
  const reqURL = new URL(base);
43291
- const inputURL = new URL(path3, reqURL);
43292
- if (path3) {
44550
+ const inputURL = new URL(path4, reqURL);
44551
+ if (path4) {
43293
44552
  reqURL.pathname += reqURL.pathname.endsWith("/") ? "" : "/";
43294
44553
  reqURL.pathname += inputURL.pathname.replace(/^\/+/, "");
43295
44554
  }
@@ -43708,9 +44967,9 @@ ${pre}${str}`;
43708
44967
  append(`┌ ${headline}:`);
43709
44968
  }
43710
44969
  for (const issue of err.issues) {
43711
- let path3 = issue.path.join(".");
43712
- path3 = path3 ? `<root>.${path3}` : "<root>";
43713
- append(`│ • [${path3}]: ${issue.message} (${issue.code})`);
44970
+ let path4 = issue.path.join(".");
44971
+ path4 = path4 ? `<root>.${path4}` : "<root>";
44972
+ append(`│ • [${path4}]: ${issue.message} (${issue.code})`);
43714
44973
  switch (issue.code) {
43715
44974
  case "invalid_literal":
43716
44975
  case "invalid_type": {
@@ -46558,7 +47817,7 @@ The Search API is a hybrid search service that combines semantic understanding w
46558
47817
  function createMCPServer(deps) {
46559
47818
  const server = new McpServer({
46560
47819
  name: "EgainMcp",
46561
- version: "1.0.1"
47820
+ version: "1.0.4"
46562
47821
  });
46563
47822
  const getClient = deps.getSDK || (() => new EgainMcpCore({
46564
47823
  security: deps.security,
@@ -47805,7 +49064,7 @@ var routes = ln({
47805
49064
  var app = _e(routes, {
47806
49065
  name: "mcp",
47807
49066
  versionInfo: {
47808
- currentVersion: "1.0.1"
49067
+ currentVersion: "1.0.4"
47809
49068
  }
47810
49069
  });
47811
49070
  Yt(app, process3.argv.slice(2), buildContext(process3));
@@ -47813,5 +49072,5 @@ export {
47813
49072
  app
47814
49073
  };
47815
49074
 
47816
- //# debugId=CFDE82BD7FF7EE8864756E2164756E21
49075
+ //# debugId=86F26C1E2362842864756E2164756E21
47817
49076
  //# sourceMappingURL=mcp-server.js.map