fa-mcp-sdk 0.2.144 → 0.2.174
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/README.md +1 -1
- package/bin/fa-mcp.js +66 -54
- package/cli-template/.env.example +2 -2
- package/cli-template/README.md +2 -2
- package/cli-template/fa-mcp-sdk-spec.md +122 -41
- package/cli-template/package.json +3 -3
- package/cli-template/r/TEST HTTP.xml +9 -0
- package/cli-template/{run/TEST SSE.run.xml → r/TEST SSE.xml } +2 -2
- package/cli-template/{run/TEST STDIO.run.xml → r/TEST STDIO.xml } +2 -2
- package/cli-template/r/generate-token.xml +14 -0
- package/cli-template/{run/kill-server.run.xml → r/kill-server.xml} +2 -2
- package/cli-template/{run/kill-token-gen-server.xml → r/remove-nul.xml} +4 -5
- package/{cli-template/config → config}/_local.yaml +28 -14
- package/{cli-template/config → config}/custom-environment-variables.yaml +3 -0
- package/{cli-template/config → config}/default.yaml +50 -10
- package/{cli-template/config → config}/development.yaml +4 -4
- package/config/local.yaml +81 -0
- package/{cli-template/config → config}/production.yaml +4 -4
- package/dist/core/_types_/active-directory-config.d.ts +3 -0
- package/dist/core/_types_/active-directory-config.d.ts.map +1 -1
- package/dist/core/_types_/config.d.ts +5 -1
- package/dist/core/_types_/config.d.ts.map +1 -1
- package/dist/core/_types_/types.d.ts +5 -1
- package/dist/core/_types_/types.d.ts.map +1 -1
- package/dist/core/ad/group-checker.d.ts +13 -0
- package/dist/core/ad/group-checker.d.ts.map +1 -0
- package/dist/core/ad/group-checker.js +86 -0
- package/dist/core/ad/group-checker.js.map +1 -0
- package/dist/core/auth/admin-auth.d.ts +16 -0
- package/dist/core/auth/admin-auth.d.ts.map +1 -0
- package/dist/core/auth/admin-auth.js +159 -0
- package/dist/core/auth/admin-auth.js.map +1 -0
- package/dist/core/auth/basic.d.ts +6 -0
- package/dist/core/auth/basic.d.ts.map +1 -0
- package/dist/core/auth/basic.js +26 -0
- package/dist/core/auth/basic.js.map +1 -0
- package/dist/core/auth/{jwt-validation.d.ts → jwt.d.ts} +4 -3
- package/dist/core/auth/jwt.d.ts.map +1 -0
- package/dist/core/auth/{jwt-validation.js → jwt.js} +9 -19
- package/dist/core/auth/jwt.js.map +1 -0
- package/dist/core/auth/middleware.d.ts.map +1 -1
- package/dist/core/auth/middleware.js +3 -3
- package/dist/core/auth/middleware.js.map +1 -1
- package/dist/core/auth/multi-auth.d.ts +14 -6
- package/dist/core/auth/multi-auth.d.ts.map +1 -1
- package/dist/core/auth/multi-auth.js +151 -141
- package/dist/core/auth/multi-auth.js.map +1 -1
- package/dist/core/auth/permanent.d.ts +6 -0
- package/dist/core/auth/permanent.d.ts.map +1 -0
- package/dist/core/auth/permanent.js +15 -0
- package/dist/core/auth/permanent.js.map +1 -0
- package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.d.ts +1 -1
- package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.d.ts.map +1 -1
- package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.js +8 -10
- package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.js.map +1 -1
- package/dist/core/auth/token-generator/ntlm/ntlm-integration.d.ts.map +1 -1
- package/dist/core/auth/token-generator/ntlm/ntlm-integration.js +9 -2
- package/dist/core/auth/token-generator/ntlm/ntlm-integration.js.map +1 -1
- package/dist/core/auth/token-generator/server.d.ts.map +1 -1
- package/dist/core/auth/token-generator/server.js +59 -25
- package/dist/core/auth/token-generator/server.js.map +1 -1
- package/dist/core/auth/types.d.ts +4 -3
- package/dist/core/auth/types.d.ts.map +1 -1
- package/dist/core/bootstrap/startup-info.d.ts.map +1 -1
- package/dist/core/bootstrap/startup-info.js +19 -0
- package/dist/core/bootstrap/startup-info.js.map +1 -1
- package/dist/core/consul/access-points-updater.js +1 -1
- package/dist/core/consul/access-points-updater.js.map +1 -1
- package/dist/core/consul/get-consul-api.d.ts +1 -1
- package/dist/core/consul/get-consul-api.d.ts.map +1 -1
- package/dist/core/consul/get-consul-api.js +1 -1
- package/dist/core/consul/get-consul-api.js.map +1 -1
- package/dist/core/consul/register.d.ts +1 -1
- package/dist/core/consul/register.d.ts.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/init-mcp-server.d.ts.map +1 -1
- package/dist/core/init-mcp-server.js +1 -1
- package/dist/core/init-mcp-server.js.map +1 -1
- package/dist/core/utils/testing/McpSseClient.js.map +1 -1
- package/dist/core/web/admin-router.d.ts +10 -0
- package/dist/core/web/admin-router.d.ts.map +1 -0
- package/dist/core/web/admin-router.js +227 -0
- package/dist/core/web/admin-router.js.map +1 -0
- package/dist/core/web/favicon-svg.d.ts +1 -1
- package/dist/core/web/favicon-svg.d.ts.map +1 -1
- package/dist/core/web/favicon-svg.js +21 -3
- package/dist/core/web/favicon-svg.js.map +1 -1
- package/dist/core/web/home-api.d.ts +7 -0
- package/dist/core/web/home-api.d.ts.map +1 -0
- package/dist/core/web/home-api.js +93 -0
- package/dist/core/web/home-api.js.map +1 -0
- package/dist/core/web/server-http.d.ts +1 -0
- package/dist/core/web/server-http.d.ts.map +1 -1
- package/dist/core/web/server-http.js +60 -25
- package/dist/core/web/server-http.js.map +1 -1
- package/dist/core/web/static/home/index.html +206 -0
- package/dist/core/web/static/home/script.js +636 -0
- package/dist/core/web/{about-page/css.js → static/styles.css} +435 -105
- package/dist/core/web/static/token-gen/index.html +82 -0
- package/dist/core/web/static/token-gen/jwt-icon.svg +3 -0
- package/dist/core/web/static/token-gen/logout.svg +4 -0
- package/dist/core/web/static/token-gen/script.js +365 -0
- package/dist/core/web/static/token-gen/user.svg +4 -0
- package/dist/core/web/svg-icons.d.ts +7 -0
- package/dist/core/web/svg-icons.d.ts.map +1 -0
- package/dist/core/web/svg-icons.js +78 -0
- package/dist/core/web/svg-icons.js.map +1 -0
- package/package.json +7 -3
- package/scripts/copy-static.js +31 -0
- package/src/template/_examples/multi-auth-examples.ts +14 -47
- package/src/template/_types_/custom-config.ts +83 -0
- package/src/template/asset/logo.svg +4 -0
- package/src/template/start.ts +3 -3
- package/src/template/tools/handle-tool-call.ts +2 -1
- package/src/tests/mcp/test-http.js +10 -2
- package/src/tests/mcp/test-sse.js +10 -2
- package/src/tests/mcp/test-stdio.js +1 -2
- package/cli-template/run/TEST HTTP.run.xml +0 -5
- package/cli-template/run/TEST search.run.xml +0 -11
- package/cli-template/run/remove-nul.js.run.xml +0 -5
- package/dist/core/auth/jwt-validation.d.ts.map +0 -1
- package/dist/core/auth/jwt-validation.js.map +0 -1
- package/dist/core/auth/token-generator/html.d.ts +0 -9
- package/dist/core/auth/token-generator/html.d.ts.map +0 -1
- package/dist/core/auth/token-generator/html.js +0 -862
- package/dist/core/auth/token-generator/html.js.map +0 -1
- package/dist/core/web/about-page/css.d.ts +0 -2
- package/dist/core/web/about-page/css.d.ts.map +0 -1
- package/dist/core/web/about-page/css.js.map +0 -1
- package/dist/core/web/about-page/render.d.ts +0 -2
- package/dist/core/web/about-page/render.d.ts.map +0 -1
- package/dist/core/web/about-page/render.js +0 -773
- package/dist/core/web/about-page/render.js.map +0 -1
- /package/cli-template/{run/== START ==.run.xml → r/== START ==.xml} +0 -0
- /package/cli-template/{run/cb.run.xml → r/cb.xml} +0 -0
- /package/cli-template/{run/ci.run.xml → r/ci.xml} +0 -0
- /package/cli-template/{run/lint.run.xml → r/lint.xml} +0 -0
- /package/cli-template/{run/lint_fix.run.xml → r/lint_fix.xml} +0 -0
- /package/cli-template/{run/reinstall.run.xml → r/reinstall.xml} +0 -0
- /package/{cli-template/config → config}/test.yaml +0 -0
- /package/{src/template/asset/favicon.svg → dist/core/web/static/logo.svg} +0 -0
- /package/{cli-template/scripts → scripts}/kill-port.js +0 -0
- /package/{cli-template/scripts → scripts}/npm/patch_node_modules.js +0 -0
- /package/{cli-template/scripts → scripts}/npm/run.js +0 -0
- /package/{cli-template/scripts → scripts}/npm/yarn-ci.ps1 +0 -0
- /package/{cli-template/scripts → scripts}/npm/yarn-ci.sh +0 -0
- /package/{cli-template/scripts → scripts}/npm/yarn-reinstall.ps1 +0 -0
- /package/{cli-template/scripts → scripts}/npm/yarn-reinstall.sh +0 -0
- /package/{cli-template/scripts → scripts}/pre-commit +0 -0
- /package/{cli-template/scripts → scripts}/remove-nul.js +0 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
// Store data globally
|
|
2
|
+
let toolsData = [];
|
|
3
|
+
let resourcesData = [];
|
|
4
|
+
let promptsData = [];
|
|
5
|
+
let pageData = {};
|
|
6
|
+
|
|
7
|
+
// Set primary color CSS variable
|
|
8
|
+
function setPrimaryColor (color) {
|
|
9
|
+
if (color) {
|
|
10
|
+
document.documentElement.style.setProperty('--primary-color', color);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Set favicon dynamically
|
|
15
|
+
function setFavicon (svgContent) {
|
|
16
|
+
if (!svgContent) {return;}
|
|
17
|
+
|
|
18
|
+
const encoded = encodeURIComponent(svgContent)
|
|
19
|
+
.replace(/'/g, '%27')
|
|
20
|
+
.replace(/"/g, '%22');
|
|
21
|
+
|
|
22
|
+
const link = document.querySelector('link[rel="icon"]') || document.createElement('link');
|
|
23
|
+
link.rel = 'icon';
|
|
24
|
+
link.type = 'image/svg+xml';
|
|
25
|
+
link.href = 'data:image/svg+xml,' + encoded;
|
|
26
|
+
|
|
27
|
+
if (!document.querySelector('link[rel="icon"]')) {
|
|
28
|
+
document.head.appendChild(link);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Set header icon dynamically
|
|
33
|
+
function setHeaderIcon (svgContent) {
|
|
34
|
+
const iconContainer = document.getElementById('serviceIcon');
|
|
35
|
+
if (iconContainer && svgContent) {
|
|
36
|
+
iconContainer.innerHTML = svgContent;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Render page info
|
|
41
|
+
function renderPageInfo (data) {
|
|
42
|
+
// Service title
|
|
43
|
+
const titleEl = document.getElementById('serviceTitle');
|
|
44
|
+
if (titleEl) {
|
|
45
|
+
titleEl.innerHTML = '<span class="MCPServer">MCP Server</span> ' + data.serviceTitle;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Document title
|
|
49
|
+
document.title = data.serviceTitle + ' MCP Server';
|
|
50
|
+
|
|
51
|
+
// Description
|
|
52
|
+
const descEl = document.getElementById('serviceDescription');
|
|
53
|
+
if (descEl) {descEl.textContent = data.description;}
|
|
54
|
+
|
|
55
|
+
// Version
|
|
56
|
+
const versionEl = document.getElementById('serviceVersion');
|
|
57
|
+
if (versionEl) {versionEl.textContent = data.version;}
|
|
58
|
+
|
|
59
|
+
// Uptime
|
|
60
|
+
const uptimeEl = document.getElementById('serviceUptime');
|
|
61
|
+
if (uptimeEl) {uptimeEl.textContent = data.uptime;}
|
|
62
|
+
|
|
63
|
+
// Tools count
|
|
64
|
+
const toolsEl = document.getElementById('toolsCount');
|
|
65
|
+
if (toolsEl) {toolsEl.textContent = data.toolsCount + ' available';}
|
|
66
|
+
|
|
67
|
+
// Resources count
|
|
68
|
+
const resourcesEl = document.getElementById('resourcesCount');
|
|
69
|
+
if (resourcesEl) {resourcesEl.textContent = data.resourcesCount + ' available';}
|
|
70
|
+
|
|
71
|
+
// Prompts count
|
|
72
|
+
const promptsEl = document.getElementById('promptsCount');
|
|
73
|
+
if (promptsEl) {promptsEl.textContent = data.promptsCount + ' available';}
|
|
74
|
+
|
|
75
|
+
// Database info
|
|
76
|
+
const dbSection = document.getElementById('dbSection');
|
|
77
|
+
if (dbSection) {
|
|
78
|
+
if (data.db) {
|
|
79
|
+
dbSection.style.display = 'block';
|
|
80
|
+
const dbValue = document.getElementById('dbValue');
|
|
81
|
+
const dbStatus = document.getElementById('dbStatus');
|
|
82
|
+
if (dbValue) {dbValue.textContent = data.db.connection + ' • ';}
|
|
83
|
+
if (dbStatus) {
|
|
84
|
+
dbStatus.textContent = data.db.status;
|
|
85
|
+
dbStatus.className = 'value ' + data.db.status;
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
dbSection.style.display = 'none';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Swagger info
|
|
93
|
+
const swaggerSection = document.getElementById('swaggerSection');
|
|
94
|
+
if (swaggerSection) {
|
|
95
|
+
swaggerSection.style.display = data.swagger ? 'block' : 'none';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Consul info
|
|
99
|
+
const consulSection = document.getElementById('consulSection');
|
|
100
|
+
if (consulSection) {
|
|
101
|
+
if (data.consul && data.consul.id) {
|
|
102
|
+
consulSection.style.display = 'block';
|
|
103
|
+
const consulLink = document.getElementById('consulLink');
|
|
104
|
+
if (consulLink) {
|
|
105
|
+
consulLink.href = data.consul.url;
|
|
106
|
+
consulLink.textContent = data.consul.id;
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
consulSection.style.display = 'none';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Footer
|
|
114
|
+
const footerContent = document.getElementById('footerContent');
|
|
115
|
+
if (footerContent && data.footer) {
|
|
116
|
+
footerContent.innerHTML = data.footer;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Load page data from API
|
|
121
|
+
async function loadPageData () {
|
|
122
|
+
try {
|
|
123
|
+
const response = await fetch('/api/home-info');
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
throw new Error('HTTP ' + response.status);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const data = await response.json();
|
|
129
|
+
pageData = data;
|
|
130
|
+
|
|
131
|
+
// Set theme color
|
|
132
|
+
setPrimaryColor(data.primaryColor);
|
|
133
|
+
|
|
134
|
+
// Set favicon and header icon (logo)
|
|
135
|
+
setFavicon(data.logoSvg);
|
|
136
|
+
setHeaderIcon(data.logoSvg);
|
|
137
|
+
|
|
138
|
+
// Store MCP data
|
|
139
|
+
toolsData = data.tools || [];
|
|
140
|
+
resourcesData = data.resources || [];
|
|
141
|
+
promptsData = data.prompts || [];
|
|
142
|
+
|
|
143
|
+
// Render page info
|
|
144
|
+
renderPageInfo(data);
|
|
145
|
+
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error('Error loading page data:', error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function openModal (sectionName) {
|
|
152
|
+
const modal = document.getElementById(sectionName + '-modal');
|
|
153
|
+
const tableBody = document.getElementById(sectionName + '-table').querySelector('tbody');
|
|
154
|
+
|
|
155
|
+
// Show loading state
|
|
156
|
+
tableBody.innerHTML = '<tr><td colspan="100%" class="loading-cell"><div class="loading-spinner"></div> Loading...</td></tr>';
|
|
157
|
+
modal.style.display = 'flex';
|
|
158
|
+
|
|
159
|
+
// Load data with small delay to show loading animation
|
|
160
|
+
setTimeout(function () {
|
|
161
|
+
loadTableData(sectionName);
|
|
162
|
+
}, 300);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function closeModal (sectionName) {
|
|
166
|
+
const modal = document.getElementById(sectionName + '-modal');
|
|
167
|
+
modal.style.display = 'none';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function loadTableData (sectionName) {
|
|
171
|
+
const tableBody = document.getElementById(sectionName + '-table').querySelector('tbody');
|
|
172
|
+
let data, html;
|
|
173
|
+
|
|
174
|
+
switch (sectionName) {
|
|
175
|
+
case 'tools':
|
|
176
|
+
data = toolsData;
|
|
177
|
+
html = generateToolsTableRows(data);
|
|
178
|
+
break;
|
|
179
|
+
case 'resources':
|
|
180
|
+
data = resourcesData;
|
|
181
|
+
html = generateResourcesTableRows(data);
|
|
182
|
+
break;
|
|
183
|
+
case 'prompts':
|
|
184
|
+
data = promptsData;
|
|
185
|
+
html = generatePromptsTableRows(data);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
tableBody.innerHTML = html;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function generateToolsTableRows (tools) {
|
|
193
|
+
if (!tools || tools.length === 0) {
|
|
194
|
+
return '<tr><td colspan="3" class="loading-cell">No tools available</td></tr>';
|
|
195
|
+
}
|
|
196
|
+
return tools.map((tool, index) =>
|
|
197
|
+
`<tr>
|
|
198
|
+
<td><code>${tool.name}</code></td>
|
|
199
|
+
<td>${tool.annotations?.title || tool.description}</td>
|
|
200
|
+
<td>
|
|
201
|
+
<a class="detail-link" id="tools-toggle-${index}" onclick="toggleDetails('tools', ${index})">details</a>
|
|
202
|
+
</td>
|
|
203
|
+
</tr>
|
|
204
|
+
<tr id="tools-detail-${index}" class="detail-row" style="display: none;">
|
|
205
|
+
<td colspan="3">
|
|
206
|
+
<div class="detail-content">
|
|
207
|
+
<div class="loading-spinner" style="display: none;"></div>
|
|
208
|
+
<pre class="json-content" style="display: none;"></pre>
|
|
209
|
+
</div>
|
|
210
|
+
</td>
|
|
211
|
+
</tr>`
|
|
212
|
+
).join('');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function generateResourcesTableRows (resources) {
|
|
216
|
+
if (!resources || resources.length === 0) {
|
|
217
|
+
return '<tr><td colspan="5" class="loading-cell">No resources available</td></tr>';
|
|
218
|
+
}
|
|
219
|
+
return resources.map((resource, index) =>
|
|
220
|
+
`<tr>
|
|
221
|
+
<td><code>${resource.uri}</code></td>
|
|
222
|
+
<td>${resource.name}</td>
|
|
223
|
+
<td>${resource.description}</td>
|
|
224
|
+
<td><code>${resource.mimeType}</code></td>
|
|
225
|
+
<td>
|
|
226
|
+
<a class="detail-link" id="resources-toggle-details-${index}" onclick="toggleResourceDetails('resources', ${index}, 'details')">details</a>
|
|
227
|
+
/
|
|
228
|
+
<a class="detail-link" id="resources-toggle-resource-${index}" onclick="toggleResourceDetails('resources', ${index}, 'resource')">resource</a>
|
|
229
|
+
</td>
|
|
230
|
+
</tr>
|
|
231
|
+
<tr id="resources-detail-${index}" class="detail-row" style="display: none;">
|
|
232
|
+
<td colspan="5">
|
|
233
|
+
<div class="detail-content">
|
|
234
|
+
<div class="loading-spinner"></div>
|
|
235
|
+
<pre class="json-content" style="display: none;"></pre>
|
|
236
|
+
<div class="resource-content" style="display: none;"></div>
|
|
237
|
+
</div>
|
|
238
|
+
</td>
|
|
239
|
+
</tr>`
|
|
240
|
+
).join('');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function generatePromptsTableRows (prompts) {
|
|
244
|
+
if (!prompts || prompts.length === 0) {
|
|
245
|
+
return '<tr><td colspan="2" class="loading-cell">No prompts available</td></tr>';
|
|
246
|
+
}
|
|
247
|
+
return prompts.map((prompt, index) =>
|
|
248
|
+
`<tr>
|
|
249
|
+
<td><code>${prompt.name}</code></td>
|
|
250
|
+
<td>
|
|
251
|
+
<a class="detail-link" id="prompts-toggle-details-${index}" onclick="togglePromptDetails('prompts', ${index}, 'details')">details</a>
|
|
252
|
+
/
|
|
253
|
+
<a class="detail-link" id="prompts-toggle-prompt-${index}" onclick="togglePromptDetails('prompts', ${index}, 'prompt')">prompt</a>
|
|
254
|
+
</td>
|
|
255
|
+
</tr>
|
|
256
|
+
<tr id="prompts-detail-${index}" class="detail-row" style="display: none;">
|
|
257
|
+
<td colspan="2">
|
|
258
|
+
<div class="detail-content">
|
|
259
|
+
<div class="loading-spinner"></div>
|
|
260
|
+
<pre class="json-content" style="display: none;"></pre>
|
|
261
|
+
<div class="prompt-content" style="display: none;"></div>
|
|
262
|
+
</div>
|
|
263
|
+
</td>
|
|
264
|
+
</tr>`
|
|
265
|
+
).join('');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function toggleDetails (sectionName, index) {
|
|
269
|
+
const detailRow = document.getElementById(sectionName + '-detail-' + index);
|
|
270
|
+
const toggleLink = document.getElementById(sectionName + '-toggle-' + index);
|
|
271
|
+
const loadingSpinner = detailRow.querySelector('.loading-spinner');
|
|
272
|
+
const jsonContent = detailRow.querySelector('.json-content');
|
|
273
|
+
|
|
274
|
+
if (detailRow.style.display === 'none') {
|
|
275
|
+
// Show the detail row with loading state
|
|
276
|
+
detailRow.style.display = 'table-row';
|
|
277
|
+
toggleLink.textContent = 'hide';
|
|
278
|
+
loadingSpinner.style.display = 'block';
|
|
279
|
+
jsonContent.style.display = 'none';
|
|
280
|
+
|
|
281
|
+
// Simulate loading delay and show content
|
|
282
|
+
setTimeout(() => {
|
|
283
|
+
let data;
|
|
284
|
+
let textContent;
|
|
285
|
+
switch (sectionName) {
|
|
286
|
+
case 'tools':
|
|
287
|
+
data = {
|
|
288
|
+
name: toolsData[index].name,
|
|
289
|
+
description: toolsData[index].description,
|
|
290
|
+
inputSchema: toolsData[index].inputSchema,
|
|
291
|
+
annotations: toolsData[index].annotations
|
|
292
|
+
};
|
|
293
|
+
textContent = JSON.stringify(data, null, 2);
|
|
294
|
+
break;
|
|
295
|
+
case 'resources':
|
|
296
|
+
data = resourcesData[index].content || resourcesData[index];
|
|
297
|
+
textContent = JSON.stringify(data, null, 2);
|
|
298
|
+
break;
|
|
299
|
+
case 'prompts':
|
|
300
|
+
data = promptsData[index];
|
|
301
|
+
textContent = JSON.stringify(data, null, 2);
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
loadingSpinner.style.display = 'none';
|
|
306
|
+
jsonContent.style.display = 'block';
|
|
307
|
+
jsonContent.textContent = textContent;
|
|
308
|
+
addCopyButton(jsonContent);
|
|
309
|
+
}, 500);
|
|
310
|
+
} else {
|
|
311
|
+
// Hide the detail row
|
|
312
|
+
detailRow.style.display = 'none';
|
|
313
|
+
toggleLink.textContent = 'details';
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Handle prompt details and prompt content display
|
|
318
|
+
async function togglePromptDetails (sectionName, index, displayType) {
|
|
319
|
+
const detailRow = document.getElementById(sectionName + '-detail-' + index);
|
|
320
|
+
const toggleLinkDetails = document.getElementById(sectionName + '-toggle-details-' + index);
|
|
321
|
+
const toggleLinkPrompt = document.getElementById(sectionName + '-toggle-prompt-' + index);
|
|
322
|
+
const loadingSpinner = detailRow.querySelector('.loading-spinner');
|
|
323
|
+
const jsonContent = detailRow.querySelector('.json-content');
|
|
324
|
+
const promptContent = detailRow.querySelector('.prompt-content');
|
|
325
|
+
|
|
326
|
+
const isCurrentlyHidden = detailRow.style.display === 'none';
|
|
327
|
+
const currentToggleLink = displayType === 'details' ? toggleLinkDetails : toggleLinkPrompt;
|
|
328
|
+
const otherToggleLink = displayType === 'details' ? toggleLinkPrompt : toggleLinkDetails;
|
|
329
|
+
|
|
330
|
+
if (isCurrentlyHidden || currentToggleLink.textContent === displayType) {
|
|
331
|
+
// Show the detail row with loading state
|
|
332
|
+
detailRow.style.display = 'table-row';
|
|
333
|
+
currentToggleLink.textContent = 'hide';
|
|
334
|
+
otherToggleLink.textContent = displayType === 'details' ? 'prompt' : 'details';
|
|
335
|
+
loadingSpinner.style.display = 'block';
|
|
336
|
+
jsonContent.style.display = 'none';
|
|
337
|
+
promptContent.style.display = 'none';
|
|
338
|
+
|
|
339
|
+
if (displayType === 'details') {
|
|
340
|
+
// Show JSON details
|
|
341
|
+
setTimeout(() => {
|
|
342
|
+
const data = promptsData[index];
|
|
343
|
+
const textContent = JSON.stringify(data, null, 2);
|
|
344
|
+
loadingSpinner.style.display = 'none';
|
|
345
|
+
jsonContent.style.display = 'block';
|
|
346
|
+
jsonContent.textContent = textContent;
|
|
347
|
+
addCopyButton(jsonContent);
|
|
348
|
+
}, 300);
|
|
349
|
+
} else {
|
|
350
|
+
// Fetch and show prompt content
|
|
351
|
+
try {
|
|
352
|
+
const promptName = promptsData[index].name;
|
|
353
|
+
const response = await fetch('/mcp', {
|
|
354
|
+
method: 'POST',
|
|
355
|
+
headers: { 'Content-Type': 'application/json' },
|
|
356
|
+
body: JSON.stringify({
|
|
357
|
+
jsonrpc: '2.0',
|
|
358
|
+
id: Date.now(),
|
|
359
|
+
method: 'prompts/get',
|
|
360
|
+
params: { name: promptName }
|
|
361
|
+
})
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
if (!response.ok) {
|
|
365
|
+
let errorData = '';
|
|
366
|
+
try {
|
|
367
|
+
errorData = await response.text();
|
|
368
|
+
} catch {
|
|
369
|
+
//
|
|
370
|
+
}
|
|
371
|
+
errorData = [response.statusText || '', errorData].join('. ');
|
|
372
|
+
throw new Error('HTTP ' + response.status + (errorData ? ': ' + errorData : ''));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const result = await response.json();
|
|
376
|
+
const messages = result.result?.messages || [];
|
|
377
|
+
let promptText = '';
|
|
378
|
+
|
|
379
|
+
messages.forEach((msg, i) => {
|
|
380
|
+
if (i > 0) {promptText += '\n\n---\n\n';}
|
|
381
|
+
promptText += 'Role: ' + msg.role + '\n\n';
|
|
382
|
+
if (typeof msg.content === 'string') {
|
|
383
|
+
promptText += msg.content;
|
|
384
|
+
} else if (msg.content?.text) {
|
|
385
|
+
promptText += msg.content.text;
|
|
386
|
+
} else {
|
|
387
|
+
promptText += JSON.stringify(msg.content, null, 2);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
loadingSpinner.style.display = 'none';
|
|
392
|
+
promptContent.style.display = 'block';
|
|
393
|
+
promptContent.innerHTML = '<pre class="json-content">' + escapeHtml(promptText) + '</pre>';
|
|
394
|
+
addCopyButton(promptContent.querySelector('.json-content'));
|
|
395
|
+
} catch (error) {
|
|
396
|
+
loadingSpinner.style.display = 'none';
|
|
397
|
+
promptContent.style.display = 'block';
|
|
398
|
+
promptContent.innerHTML = '<div class="error-message">Failed to load prompt: ' + escapeHtml(error.message) + '</div>';
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
} else {
|
|
402
|
+
// Hide the detail row
|
|
403
|
+
detailRow.style.display = 'none';
|
|
404
|
+
toggleLinkDetails.textContent = 'details';
|
|
405
|
+
toggleLinkPrompt.textContent = 'prompt';
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Handle resource details and resource content display
|
|
410
|
+
async function toggleResourceDetails (sectionName, index, displayType) {
|
|
411
|
+
const detailRow = document.getElementById(sectionName + '-detail-' + index);
|
|
412
|
+
const toggleLinkDetails = document.getElementById(sectionName + '-toggle-details-' + index);
|
|
413
|
+
const toggleLinkResource = document.getElementById(sectionName + '-toggle-resource-' + index);
|
|
414
|
+
const loadingSpinner = detailRow.querySelector('.loading-spinner');
|
|
415
|
+
const jsonContent = detailRow.querySelector('.json-content');
|
|
416
|
+
const resourceContent = detailRow.querySelector('.resource-content');
|
|
417
|
+
|
|
418
|
+
const isCurrentlyHidden = detailRow.style.display === 'none';
|
|
419
|
+
const currentToggleLink = displayType === 'details' ? toggleLinkDetails : toggleLinkResource;
|
|
420
|
+
const otherToggleLink = displayType === 'details' ? toggleLinkResource : toggleLinkDetails;
|
|
421
|
+
|
|
422
|
+
if (isCurrentlyHidden || currentToggleLink.textContent === displayType) {
|
|
423
|
+
// Show the detail row with loading state
|
|
424
|
+
detailRow.style.display = 'table-row';
|
|
425
|
+
currentToggleLink.textContent = 'hide';
|
|
426
|
+
otherToggleLink.textContent = displayType === 'details' ? 'resource' : 'details';
|
|
427
|
+
loadingSpinner.style.display = 'block';
|
|
428
|
+
jsonContent.style.display = 'none';
|
|
429
|
+
resourceContent.style.display = 'none';
|
|
430
|
+
|
|
431
|
+
if (displayType === 'details') {
|
|
432
|
+
// Show JSON details
|
|
433
|
+
setTimeout(() => {
|
|
434
|
+
const data = resourcesData[index];
|
|
435
|
+
const textContent = JSON.stringify(data, null, 2);
|
|
436
|
+
loadingSpinner.style.display = 'none';
|
|
437
|
+
jsonContent.style.display = 'block';
|
|
438
|
+
jsonContent.textContent = textContent;
|
|
439
|
+
addCopyButton(jsonContent);
|
|
440
|
+
}, 300);
|
|
441
|
+
} else {
|
|
442
|
+
// Fetch and show resource content
|
|
443
|
+
try {
|
|
444
|
+
const resourceUri = resourcesData[index].uri;
|
|
445
|
+
const response = await fetch('/mcp', {
|
|
446
|
+
method: 'POST',
|
|
447
|
+
headers: { 'Content-Type': 'application/json' },
|
|
448
|
+
body: JSON.stringify({
|
|
449
|
+
jsonrpc: '2.0',
|
|
450
|
+
id: Date.now(),
|
|
451
|
+
method: 'resources/read',
|
|
452
|
+
params: { uri: resourceUri }
|
|
453
|
+
})
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
if (!response.ok) {
|
|
457
|
+
let errorData = '';
|
|
458
|
+
try {
|
|
459
|
+
errorData = await response.text();
|
|
460
|
+
} catch {
|
|
461
|
+
//
|
|
462
|
+
}
|
|
463
|
+
errorData = [response.statusText || '', errorData].join('. ');
|
|
464
|
+
throw new Error('HTTP ' + response.status + (errorData ? ': ' + errorData : ''));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const result = await response.json();
|
|
468
|
+
const contents = result.result?.contents || [];
|
|
469
|
+
let resourceText = '';
|
|
470
|
+
|
|
471
|
+
contents.forEach((content, i) => {
|
|
472
|
+
if (i > 0) {resourceText += '\n\n---\n\n';}
|
|
473
|
+
resourceText += 'URI: ' + content.uri + '\n';
|
|
474
|
+
resourceText += 'MIME Type: ' + content.mimeType + '\n\n';
|
|
475
|
+
|
|
476
|
+
if (content.text) {
|
|
477
|
+
let processedText = content.text;
|
|
478
|
+
|
|
479
|
+
// Handle JSON content more intelligently
|
|
480
|
+
if (content.mimeType === 'application/json') {
|
|
481
|
+
if (typeof processedText !== 'string') {
|
|
482
|
+
processedText = JSON.stringify(processedText, null, 2);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
resourceText += processedText;
|
|
486
|
+
} else if (content.blob) {
|
|
487
|
+
resourceText += '[Binary content: ' + content.blob.length + ' bytes]';
|
|
488
|
+
} else {
|
|
489
|
+
resourceText += JSON.stringify(content, null, 2);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
loadingSpinner.style.display = 'none';
|
|
494
|
+
resourceContent.style.display = 'block';
|
|
495
|
+
resourceContent.innerHTML = '<pre class="json-content">' + escapeHtml(resourceText) + '</pre>';
|
|
496
|
+
addCopyButton(resourceContent.querySelector('.json-content'));
|
|
497
|
+
} catch (error) {
|
|
498
|
+
loadingSpinner.style.display = 'none';
|
|
499
|
+
resourceContent.style.display = 'block';
|
|
500
|
+
resourceContent.innerHTML = '<div class="error-message">Failed to load resource: ' + escapeHtml(error.message) + '</div>';
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
} else {
|
|
504
|
+
// Hide the detail row
|
|
505
|
+
detailRow.style.display = 'none';
|
|
506
|
+
toggleLinkDetails.textContent = 'details';
|
|
507
|
+
toggleLinkResource.textContent = 'resource';
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Health Check Modal
|
|
512
|
+
async function openHealthCheckModal () {
|
|
513
|
+
const modal = document.getElementById('health-modal');
|
|
514
|
+
const loading = document.getElementById('health-loading');
|
|
515
|
+
const result = document.getElementById('health-result');
|
|
516
|
+
const error = document.getElementById('health-error');
|
|
517
|
+
|
|
518
|
+
// Show modal with loading state
|
|
519
|
+
modal.style.display = 'flex';
|
|
520
|
+
loading.style.display = 'block';
|
|
521
|
+
result.style.display = 'none';
|
|
522
|
+
error.style.display = 'none';
|
|
523
|
+
|
|
524
|
+
try {
|
|
525
|
+
const response = await fetch('/health');
|
|
526
|
+
|
|
527
|
+
if (!response.ok) {
|
|
528
|
+
let errorData = '';
|
|
529
|
+
try {
|
|
530
|
+
errorData = await response.text();
|
|
531
|
+
} catch {
|
|
532
|
+
//
|
|
533
|
+
}
|
|
534
|
+
errorData = [response.statusText || '', errorData].join('. ');
|
|
535
|
+
throw new Error('HTTP ' + response.status + (errorData ? ': ' + errorData : ''));
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const data = await response.json();
|
|
539
|
+
|
|
540
|
+
// Hide loading and show result
|
|
541
|
+
loading.style.display = 'none';
|
|
542
|
+
result.style.display = 'block';
|
|
543
|
+
result.textContent = JSON.stringify(data, null, 2);
|
|
544
|
+
addCopyButton(result);
|
|
545
|
+
} catch (err) {
|
|
546
|
+
// Hide loading and show error
|
|
547
|
+
loading.style.display = 'none';
|
|
548
|
+
error.style.display = 'block';
|
|
549
|
+
error.textContent = 'Error: ' + (err.message || 'Failed to fetch health check data');
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Escape HTML to prevent XSS
|
|
554
|
+
function escapeHtml (text) {
|
|
555
|
+
const div = document.createElement('div');
|
|
556
|
+
div.textContent = text;
|
|
557
|
+
return div.innerHTML;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Copy to clipboard functionality
|
|
561
|
+
function addCopyButton (contentElement) {
|
|
562
|
+
if (!contentElement || contentElement.hasAttribute('data-copy-added')) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
contentElement.setAttribute('data-copy-added', 'true');
|
|
567
|
+
|
|
568
|
+
const copyButton = document.createElement('button');
|
|
569
|
+
copyButton.className = 'copy-button';
|
|
570
|
+
copyButton.innerHTML = '📋';
|
|
571
|
+
copyButton.title = 'Copy to clipboard';
|
|
572
|
+
copyButton.setAttribute('aria-label', 'Copy to clipboard');
|
|
573
|
+
|
|
574
|
+
const notification = document.createElement('div');
|
|
575
|
+
notification.className = 'copy-notification';
|
|
576
|
+
notification.textContent = 'Copied';
|
|
577
|
+
|
|
578
|
+
contentElement.appendChild(copyButton);
|
|
579
|
+
contentElement.appendChild(notification);
|
|
580
|
+
|
|
581
|
+
copyButton.addEventListener('click', async function () {
|
|
582
|
+
let textToCopy = contentElement.textContent || contentElement.innerText;
|
|
583
|
+
textToCopy = textToCopy.replace(/📋Copied/, '');
|
|
584
|
+
try {
|
|
585
|
+
await navigator.clipboard.writeText(textToCopy);
|
|
586
|
+
|
|
587
|
+
// Show notification
|
|
588
|
+
notification.classList.add('show');
|
|
589
|
+
|
|
590
|
+
// Hide notification after 1 second
|
|
591
|
+
setTimeout(() => {
|
|
592
|
+
notification.classList.remove('show');
|
|
593
|
+
}, 1000);
|
|
594
|
+
|
|
595
|
+
} catch (err) {
|
|
596
|
+
// Fallback for browsers that don't support clipboard API
|
|
597
|
+
const textArea = document.createElement('textarea');
|
|
598
|
+
textArea.value = textToCopy;
|
|
599
|
+
textArea.style.position = 'fixed';
|
|
600
|
+
textArea.style.opacity = '0';
|
|
601
|
+
document.body.appendChild(textArea);
|
|
602
|
+
textArea.focus();
|
|
603
|
+
textArea.select();
|
|
604
|
+
|
|
605
|
+
try {
|
|
606
|
+
document.execCommand('copy');
|
|
607
|
+
|
|
608
|
+
// Show notification
|
|
609
|
+
notification.classList.add('show');
|
|
610
|
+
|
|
611
|
+
// Hide notification after 1 second
|
|
612
|
+
setTimeout(() => {
|
|
613
|
+
notification.classList.remove('show');
|
|
614
|
+
}, 1000);
|
|
615
|
+
} catch (fallbackErr) {
|
|
616
|
+
console.error('Failed to copy text:', fallbackErr);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
document.body.removeChild(textArea);
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Close modal when clicking outside
|
|
625
|
+
document.addEventListener('click', function (event) {
|
|
626
|
+
if (event.target.classList.contains('modal-overlay')) {
|
|
627
|
+
const modalId = event.target.id;
|
|
628
|
+
const sectionName = modalId.replace('-modal', '');
|
|
629
|
+
closeModal(sectionName);
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Initialize on page load
|
|
634
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
635
|
+
loadPageData();
|
|
636
|
+
});
|