jxp 4.2.0 → 5.0.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 (104) hide show
  1. package/.env.sample +16 -0
  2. package/README.md +2 -0
  3. package/dist/bin/mcp-stdio-bridge.d.ts +3 -0
  4. package/dist/bin/mcp-stdio-bridge.d.ts.map +1 -0
  5. package/dist/bin/mcp-stdio-bridge.js +87 -0
  6. package/dist/bin/mcp-stdio-bridge.js.map +1 -0
  7. package/dist/bin/server.d.ts.map +1 -1
  8. package/dist/bin/server.js +2 -1
  9. package/dist/bin/server.js.map +1 -1
  10. package/dist/globals.d.ts +2 -2
  11. package/dist/globals.d.ts.map +1 -1
  12. package/dist/libs/docs-auth.d.ts.map +1 -1
  13. package/dist/libs/docs-auth.js +4 -2
  14. package/dist/libs/docs-auth.js.map +1 -1
  15. package/dist/libs/docs.js +74 -43
  16. package/dist/libs/docs.js.map +1 -1
  17. package/dist/libs/jxp.d.ts.map +1 -1
  18. package/dist/libs/jxp.js +22 -321
  19. package/dist/libs/jxp.js.map +1 -1
  20. package/dist/libs/load-config.d.ts +1 -1
  21. package/dist/libs/load-config.d.ts.map +1 -1
  22. package/dist/libs/load-config.js +2 -2
  23. package/dist/libs/load-config.js.map +1 -1
  24. package/dist/libs/mcp/auth.d.ts +4 -0
  25. package/dist/libs/mcp/auth.d.ts.map +1 -0
  26. package/dist/libs/mcp/auth.js +49 -0
  27. package/dist/libs/mcp/auth.js.map +1 -0
  28. package/dist/libs/mcp/config.d.ts +21 -0
  29. package/dist/libs/mcp/config.d.ts.map +1 -0
  30. package/dist/libs/mcp/config.js +66 -0
  31. package/dist/libs/mcp/config.js.map +1 -0
  32. package/dist/libs/mcp/context.d.ts +5 -0
  33. package/dist/libs/mcp/context.d.ts.map +1 -0
  34. package/dist/libs/mcp/context.js +14 -0
  35. package/dist/libs/mcp/context.js.map +1 -0
  36. package/dist/libs/mcp/describe.d.ts +13 -0
  37. package/dist/libs/mcp/describe.d.ts.map +1 -0
  38. package/dist/libs/mcp/describe.js +36 -0
  39. package/dist/libs/mcp/describe.js.map +1 -0
  40. package/dist/libs/mcp/docs_auth.d.ts +5 -0
  41. package/dist/libs/mcp/docs_auth.d.ts.map +1 -0
  42. package/dist/libs/mcp/docs_auth.js +36 -0
  43. package/dist/libs/mcp/docs_auth.js.map +1 -0
  44. package/dist/libs/mcp/dynamic_import.d.ts +6 -0
  45. package/dist/libs/mcp/dynamic_import.d.ts.map +1 -0
  46. package/dist/libs/mcp/dynamic_import.js +12 -0
  47. package/dist/libs/mcp/dynamic_import.js.map +1 -0
  48. package/dist/libs/mcp/execute_tool.d.ts +10 -0
  49. package/dist/libs/mcp/execute_tool.d.ts.map +1 -0
  50. package/dist/libs/mcp/execute_tool.js +65 -0
  51. package/dist/libs/mcp/execute_tool.js.map +1 -0
  52. package/dist/libs/mcp/guides/default.md +67 -0
  53. package/dist/libs/mcp/guides.d.ts +14 -0
  54. package/dist/libs/mcp/guides.d.ts.map +1 -0
  55. package/dist/libs/mcp/guides.js +127 -0
  56. package/dist/libs/mcp/guides.js.map +1 -0
  57. package/dist/libs/mcp/model_visibility.d.ts +31 -0
  58. package/dist/libs/mcp/model_visibility.d.ts.map +1 -0
  59. package/dist/libs/mcp/model_visibility.js +73 -0
  60. package/dist/libs/mcp/model_visibility.js.map +1 -0
  61. package/dist/libs/mcp/mount.d.ts +10 -0
  62. package/dist/libs/mcp/mount.d.ts.map +1 -0
  63. package/dist/libs/mcp/mount.js +68 -0
  64. package/dist/libs/mcp/mount.js.map +1 -0
  65. package/dist/libs/mcp/read_service.d.ts +23 -0
  66. package/dist/libs/mcp/read_service.d.ts.map +1 -0
  67. package/dist/libs/mcp/read_service.js +141 -0
  68. package/dist/libs/mcp/read_service.js.map +1 -0
  69. package/dist/libs/mcp/server.d.ts +23 -0
  70. package/dist/libs/mcp/server.d.ts.map +1 -0
  71. package/dist/libs/mcp/server.js +111 -0
  72. package/dist/libs/mcp/server.js.map +1 -0
  73. package/dist/libs/read_handlers.d.ts +13 -0
  74. package/dist/libs/read_handlers.d.ts.map +1 -0
  75. package/dist/libs/read_handlers.js +381 -0
  76. package/dist/libs/read_handlers.js.map +1 -0
  77. package/dist/libs/request_log.d.ts +2 -0
  78. package/dist/libs/request_log.d.ts.map +1 -1
  79. package/dist/libs/request_log.js +7 -1
  80. package/dist/libs/request_log.js.map +1 -1
  81. package/dist/libs/schema.d.ts.map +1 -1
  82. package/dist/libs/schema.js +5 -8
  83. package/dist/libs/schema.js.map +1 -1
  84. package/dist/libs/schema_description.d.ts +7 -1
  85. package/dist/libs/schema_description.d.ts.map +1 -1
  86. package/dist/libs/schema_description.js +24 -25
  87. package/dist/libs/schema_description.js.map +1 -1
  88. package/dist/libs/schema_serialize.d.ts +3 -0
  89. package/dist/libs/schema_serialize.d.ts.map +1 -0
  90. package/dist/libs/schema_serialize.js +45 -0
  91. package/dist/libs/schema_serialize.js.map +1 -0
  92. package/dist/types/jxp-config.d.ts +7 -0
  93. package/dist/types/jxp-config.d.ts.map +1 -1
  94. package/docs/changelog.md +61 -0
  95. package/docs/index.md +1 -0
  96. package/docs/mcp.md +134 -0
  97. package/mkdocs.yml +1 -0
  98. package/package.json +31 -26
  99. package/templates/assets/docs.css +107 -0
  100. package/templates/assets/mcp-console.js +343 -0
  101. package/templates/index.pug +37 -0
  102. package/templates/mcp.pug +112 -0
  103. package/templates/navbar.pug +2 -0
  104. package/templates/sidebar.pug +4 -0
@@ -0,0 +1,343 @@
1
+ (function () {
2
+ "use strict";
3
+
4
+ const STORAGE_KEY = "jxp_docs_api_key";
5
+ const STORAGE_REMEMBER = "jxp_docs_remember_key";
6
+
7
+ function getApiKey() {
8
+ const input = document.getElementById("docs-api-key");
9
+ return input ? input.value.trim() : "";
10
+ }
11
+
12
+ function loadStoredKey() {
13
+ if (localStorage.getItem(STORAGE_REMEMBER) !== "1") return;
14
+ const key = localStorage.getItem(STORAGE_KEY);
15
+ if (!key) return;
16
+ const input = document.getElementById("docs-api-key");
17
+ const remember = document.getElementById("docs-remember-key");
18
+ if (input) input.value = key;
19
+ if (remember) remember.checked = true;
20
+ }
21
+
22
+ function saveKeyIfRemembered() {
23
+ const remember = document.getElementById("docs-remember-key");
24
+ const input = document.getElementById("docs-api-key");
25
+ if (!remember || !input) return;
26
+ if (remember.checked) {
27
+ localStorage.setItem(STORAGE_REMEMBER, "1");
28
+ localStorage.setItem(STORAGE_KEY, input.value.trim());
29
+ } else {
30
+ localStorage.removeItem(STORAGE_REMEMBER);
31
+ localStorage.removeItem(STORAGE_KEY);
32
+ }
33
+ updateMcpConfigSnippets();
34
+ }
35
+
36
+ function getMcpSetup() {
37
+ const card = document.querySelector(".mcp-setup-card");
38
+ const fromWindow = window.__MCP_SETUP__ || {};
39
+ const baseUrl =
40
+ fromWindow.baseUrl ||
41
+ card?.dataset.baseUrl ||
42
+ window.location.origin;
43
+ const mcpPath = fromWindow.mcpPath || card?.dataset.mcpPath || "/mcp";
44
+ const base = String(baseUrl).replace(/\/$/, "");
45
+ const path = mcpPath.startsWith("/") ? mcpPath : `/${mcpPath}`;
46
+ const mcpUrl = fromWindow.mcpUrl || card?.dataset.mcpUrl || `${base}${path}`;
47
+ return {
48
+ baseUrl: base,
49
+ mcpUrl,
50
+ serverSlug: fromWindow.serverSlug || card?.dataset.serverSlug || "jxp",
51
+ };
52
+ }
53
+
54
+ function buildMcpJsonConfig(kind, apiKey) {
55
+ const setup = getMcpSetup();
56
+ const key = apiKey || "YOUR_API_KEY";
57
+ const slug = setup.serverSlug || "jxp";
58
+ if (kind === "stdio") {
59
+ return JSON.stringify(
60
+ {
61
+ mcpServers: {
62
+ [slug]: {
63
+ command: "npx",
64
+ args: ["-y", "jxp-mcp"],
65
+ env: {
66
+ JXP_URL: setup.baseUrl,
67
+ JXP_API_KEY: key,
68
+ },
69
+ },
70
+ },
71
+ },
72
+ null,
73
+ 2
74
+ );
75
+ }
76
+ return JSON.stringify(
77
+ {
78
+ mcpServers: {
79
+ [slug]: {
80
+ url: setup.mcpUrl,
81
+ headers: { "X-API-Key": key },
82
+ },
83
+ },
84
+ },
85
+ null,
86
+ 2
87
+ );
88
+ }
89
+
90
+ function updateMcpConfigSnippets() {
91
+ const apiKey = getApiKey();
92
+ const httpEl = document.getElementById("mcpJsonHttp");
93
+ const stdioEl = document.getElementById("mcpJsonStdio");
94
+ if (httpEl) httpEl.textContent = buildMcpJsonConfig("http", apiKey);
95
+ if (stdioEl) stdioEl.textContent = buildMcpJsonConfig("stdio", apiKey);
96
+ }
97
+
98
+ async function copyMcpConfig(targetId, btn) {
99
+ const el = document.getElementById(targetId);
100
+ if (!el) return;
101
+ try {
102
+ await navigator.clipboard.writeText(el.textContent || "");
103
+ const label = btn.innerHTML;
104
+ btn.innerHTML = '<i class="bi bi-check2 me-1"></i>Copied';
105
+ setTimeout(function () {
106
+ btn.innerHTML = label;
107
+ }, 1500);
108
+ } catch {
109
+ /* ignore */
110
+ }
111
+ }
112
+
113
+ function escapeHtml(text) {
114
+ return String(text)
115
+ .replace(/&/g, "&amp;")
116
+ .replace(/</g, "&lt;")
117
+ .replace(/>/g, "&gt;")
118
+ .replace(/"/g, "&quot;");
119
+ }
120
+
121
+ function formatBody(text) {
122
+ if (!text) return "";
123
+ try {
124
+ return JSON.stringify(JSON.parse(text), null, 2);
125
+ } catch {
126
+ return text;
127
+ }
128
+ }
129
+
130
+ function appendMessage(thread, role, bodyHtml, isError) {
131
+ const wrap = document.createElement("div");
132
+ wrap.className = "mcp-msg mcp-msg-" + role + (isError ? " mcp-msg-error" : "");
133
+ const label = document.createElement("div");
134
+ label.className = "mcp-msg-label";
135
+ label.textContent = role === "user" ? "You" : "Assistant";
136
+ const body = document.createElement("div");
137
+ body.className = "mcp-msg-body";
138
+ body.innerHTML = bodyHtml;
139
+ wrap.appendChild(label);
140
+ wrap.appendChild(body);
141
+ thread.appendChild(wrap);
142
+ thread.scrollTop = thread.scrollHeight;
143
+ }
144
+
145
+ function appendToolNote(thread, toolName, args) {
146
+ const argsText = Object.keys(args).length ? JSON.stringify(args) : "{}";
147
+ appendMessage(
148
+ thread,
149
+ "user",
150
+ `<p class="mb-0"><code>${escapeHtml(toolName)}</code> <span class="text-muted">${escapeHtml(argsText)}</span></p>`,
151
+ false
152
+ );
153
+ }
154
+
155
+ function appendResult(thread, text, isError) {
156
+ const formatted = escapeHtml(formatBody(text));
157
+ appendMessage(thread, "bot", `<pre class="mcp-result mb-0">${formatted}</pre>`, isError);
158
+ }
159
+
160
+ async function callMcpTool(name, args) {
161
+ const headers = { "Content-Type": "application/json", Accept: "application/json" };
162
+ const apiKey = getApiKey();
163
+ if (apiKey) headers["X-API-Key"] = apiKey;
164
+
165
+ const res = await fetch("/docs/mcp/call", {
166
+ method: "POST",
167
+ credentials: "same-origin",
168
+ headers,
169
+ body: JSON.stringify({ name, arguments: args }),
170
+ });
171
+ const payload = await res.json().catch(() => ({ text: res.statusText, isError: true }));
172
+ if (!res.ok) {
173
+ const msg = payload.message || payload.text || res.statusText;
174
+ throw new Error(msg);
175
+ }
176
+ return payload;
177
+ }
178
+
179
+ async function runTool(thread, name, args) {
180
+ appendToolNote(thread, name, args);
181
+ try {
182
+ const result = await callMcpTool(name, args);
183
+ appendResult(thread, result.text || "", !!result.isError);
184
+ } catch (err) {
185
+ appendResult(thread, String(err.message || err), true);
186
+ }
187
+ }
188
+
189
+ function parseLimit(text) {
190
+ const match = text.match(/\blimit\s*=\s*(\d+)/i);
191
+ return match ? parseInt(match[1], 10) : 5;
192
+ }
193
+
194
+ function parseCommand(input) {
195
+ const text = input.trim();
196
+ if (!text) return null;
197
+
198
+ const lower = text.toLowerCase();
199
+ if (lower === "help" || lower === "?") {
200
+ return {
201
+ help: true,
202
+ html:
203
+ "<p>Commands:</p><ul class=\"mb-0\"><li><code>list models</code></li>" +
204
+ "<li><code>describe MODEL</code></li><li><code>count MODEL</code></li>" +
205
+ "<li><code>find MODEL</code> or <code>find MODEL limit=10</code></li></ul>",
206
+ };
207
+ }
208
+
209
+ if (/^list\s+models?$/i.test(text)) {
210
+ return { tool: "jxp_list_models", args: {} };
211
+ }
212
+
213
+ const describeMatch = text.match(/^describe\s+(\S+)/i);
214
+ if (describeMatch) {
215
+ return { tool: "jxp_describe_model", args: { model: describeMatch[1] } };
216
+ }
217
+
218
+ const countMatch = text.match(/^count\s+(\S+)/i);
219
+ if (countMatch) {
220
+ return { tool: "jxp_count", args: { model: countMatch[1] } };
221
+ }
222
+
223
+ const findMatch = text.match(/^find\s+(\S+)/i);
224
+ if (findMatch) {
225
+ return {
226
+ tool: "jxp_find",
227
+ args: { model: findMatch[1], limit: parseLimit(text) },
228
+ };
229
+ }
230
+
231
+ return {
232
+ error:
233
+ "I didn't understand that. Try <code>list models</code>, <code>describe MODEL</code>, " +
234
+ "<code>count MODEL</code>, or <code>find MODEL limit=5</code>. Type <code>help</code> for more.",
235
+ };
236
+ }
237
+
238
+ async function handleUserInput(thread, input) {
239
+ const text = input.trim();
240
+ if (!text) return;
241
+
242
+ appendMessage(thread, "user", `<p class="mb-0">${escapeHtml(text)}</p>`, false);
243
+
244
+ const parsed = parseCommand(text);
245
+ if (!parsed) return;
246
+
247
+ if (parsed.help) {
248
+ appendMessage(thread, "bot", parsed.html, false);
249
+ return;
250
+ }
251
+ if (parsed.error) {
252
+ appendMessage(thread, "bot", `<p class="mb-0">${parsed.error}</p>`, true);
253
+ return;
254
+ }
255
+
256
+ await runTool(thread, parsed.tool, parsed.args);
257
+ }
258
+
259
+ async function loadSessionApiKey() {
260
+ const access = document.documentElement.dataset.docsAccess;
261
+ if (access !== "protected") return;
262
+ try {
263
+ const res = await fetch("/docs/session", { credentials: "same-origin" });
264
+ if (!res.ok) return;
265
+ const data = await res.json();
266
+ if (!data.apikey) return;
267
+ const input = document.getElementById("docs-api-key");
268
+ if (input) input.value = data.apikey;
269
+ updateMcpConfigSnippets();
270
+ } catch {
271
+ /* ignore */
272
+ }
273
+ }
274
+
275
+ document.addEventListener("DOMContentLoaded", function () {
276
+ const access = document.documentElement.dataset.docsAccess;
277
+ const thread = document.getElementById("mcpChatThread");
278
+ const form = document.getElementById("mcpChatForm");
279
+ const chatInput = document.getElementById("mcpChatInput");
280
+ const toolSelect = document.getElementById("mcpToolSelect");
281
+ const toolArgs = document.getElementById("mcpToolArgs");
282
+ const toolRun = document.getElementById("mcpToolRun");
283
+
284
+ if (!thread) return;
285
+
286
+ loadSessionApiKey().then(function () {
287
+ if (access !== "protected") loadStoredKey();
288
+ updateMcpConfigSnippets();
289
+ });
290
+
291
+ const keyInput = document.getElementById("docs-api-key");
292
+ const remember = document.getElementById("docs-remember-key");
293
+ if (keyInput) {
294
+ keyInput.addEventListener("change", saveKeyIfRemembered);
295
+ keyInput.addEventListener("input", updateMcpConfigSnippets);
296
+ }
297
+ if (remember) remember.addEventListener("change", saveKeyIfRemembered);
298
+
299
+ document.querySelectorAll(".mcp-copy-btn").forEach(function (btn) {
300
+ btn.addEventListener("click", function () {
301
+ const target = btn.getAttribute("data-target");
302
+ if (target) copyMcpConfig(target, btn);
303
+ });
304
+ });
305
+
306
+ if (form && chatInput) {
307
+ form.addEventListener("submit", function (e) {
308
+ e.preventDefault();
309
+ saveKeyIfRemembered();
310
+ const value = chatInput.value;
311
+ chatInput.value = "";
312
+ handleUserInput(thread, value);
313
+ });
314
+ }
315
+
316
+ document.querySelectorAll(".mcp-quick-btn").forEach(function (btn) {
317
+ btn.addEventListener("click", function () {
318
+ saveKeyIfRemembered();
319
+ const cmd = btn.getAttribute("data-cmd") || "";
320
+ if (chatInput) chatInput.value = cmd;
321
+ handleUserInput(thread, cmd);
322
+ if (chatInput) chatInput.value = "";
323
+ });
324
+ });
325
+
326
+ if (toolRun && toolSelect && toolArgs) {
327
+ toolRun.addEventListener("click", function () {
328
+ saveKeyIfRemembered();
329
+ let args = {};
330
+ const raw = toolArgs.value.trim();
331
+ if (raw) {
332
+ try {
333
+ args = JSON.parse(raw);
334
+ } catch (err) {
335
+ appendResult(thread, "Invalid JSON arguments: " + err.message, true);
336
+ return;
337
+ }
338
+ }
339
+ runTool(thread, toolSelect.value, args);
340
+ });
341
+ }
342
+ });
343
+ })();
@@ -11,6 +11,12 @@ block content
11
11
  if first_guide_url
12
12
  a.btn.btn-primary(href=first_guide_url) Get started
13
13
  a.btn.btn-outline-primary(href="/docs/api") Browse API
14
+ if mcp_enabled
15
+ a.btn.btn-outline-primary(href="/docs/mcp")
16
+ i.bi.bi-robot.me-1
17
+ | Ask MCP
18
+ else
19
+ a.btn.btn-outline-secondary(href="/docs/md/mcp.md") MCP setup
14
20
  a.btn.btn-outline-secondary(href="/docs/diagnostics") Index diagnostics
15
21
  a.btn.btn-outline-secondary(href="/docs/md/api.md") REST reference
16
22
 
@@ -35,6 +41,37 @@ block content
35
41
  | panels on API resource pages. Keys are stored in the browser only when “Remember” is checked.
36
42
  .col-md-6
37
43
  .card.h-100
44
+ .card-header.fw-semibold
45
+ if mcp_enabled
46
+ | MCP for AI agents
47
+ else
48
+ | MCP (Model Context Protocol)
49
+ .card-body
50
+ if mcp_enabled
51
+ p.mb-2
52
+ | This server exposes read-only MCP tools at
53
+ code= mcp_path
54
+ | — use them from Cursor, LM Studio, or the built-in assistant.
55
+ pre.mb-3
56
+ code.
57
+ MCP_ENABLED=true
58
+ # Cursor: npx -y jxp-mcp
59
+ a.btn.btn-sm.btn-primary(href="/docs/mcp") Open MCP assistant
60
+ a.btn.btn-sm.btn-outline-secondary.ms-2(href="/docs/md/mcp.md") Setup guide
61
+ else
62
+ p.mb-2
63
+ | Enable
64
+ code MCP_ENABLED=true
65
+ | to expose five fixed read-only tools (
66
+ code jxp_list_models
67
+ | ,
68
+ code jxp_find
69
+ | , …) for AI clients and a built-in docs playground.
70
+ a.btn.btn-sm.btn-outline-primary(href="/docs/md/mcp.md") Read MCP docs
71
+
72
+ .row.g-4.mb-4
73
+ .col-12
74
+ .card
38
75
  .card-header.fw-semibold API at a glance
39
76
  .card-body
40
77
  p.mb-2
@@ -0,0 +1,112 @@
1
+ extends layout
2
+
3
+ block content
4
+ h1.mb-1 MCP assistant
5
+ p.text-muted.mb-4
6
+ | Connect Cursor or LM Studio to this API, then try the playground below.
7
+
8
+ .card.mb-4.mcp-setup-card(
9
+ data-mcp-url=mcp_url
10
+ data-base-url=base_url
11
+ data-mcp-path=mcp_path
12
+ data-server-slug=mcp_server_slug
13
+ )
14
+ .card-header.fw-semibold
15
+ i.bi.bi-plug.me-1
16
+ | Add to your MCP config
17
+ .card-body
18
+ p.mb-2
19
+ | Merge into
20
+ code mcp.json
21
+ | (Cursor:
22
+ strong Cursor Settings → MCP
23
+ | ; LM Studio: server settings). Your API key is filled in from your session.
24
+ ul.nav.nav-tabs#mcpConfigTabs(role="tablist")
25
+ li.nav-item(role="presentation")
26
+ button.nav-link.active#mcp-http-tab(data-bs-toggle="tab" data-bs-target="#mcp-http-pane" type="button" role="tab" aria-controls="mcp-http-pane" aria-selected="true") Cursor / HTTP
27
+ li.nav-item(role="presentation")
28
+ button.nav-link#mcp-stdio-tab(data-bs-toggle="tab" data-bs-target="#mcp-stdio-pane" type="button" role="tab" aria-controls="mcp-stdio-pane" aria-selected="false") stdio (jxp-mcp)
29
+ .tab-content.border.border-top-0.rounded-bottom.p-3.bg-light#mcpConfigTabContent
30
+ .tab-pane.fade.show.active#mcp-http-pane(role="tabpanel" aria-labelledby="mcp-http-tab" tabindex="0")
31
+ p.small.text-muted.mb-2 Recommended for Cursor — receives server instructions on connect.
32
+ .position-relative
33
+ pre.mcp-json-block.mb-2#mcpJsonHttp(data-config="http")= mcp_http_json
34
+ button.btn.btn-sm.btn-outline-secondary.mcp-copy-btn(type="button" data-target="mcpJsonHttp")
35
+ i.bi.bi-clipboard.me-1
36
+ | Copy
37
+ .tab-pane.fade#mcp-stdio-pane(role="tabpanel" aria-labelledby="mcp-stdio-tab" tabindex="0")
38
+ p.small.text-muted.mb-2 Uses
39
+ code npx jxp-mcp
40
+ | to proxy stdio → HTTP (tools, resources, instructions).
41
+ .position-relative
42
+ pre.mcp-json-block.mb-2#mcpJsonStdio(data-config="stdio")= mcp_stdio_json
43
+ button.btn.btn-sm.btn-outline-secondary.mcp-copy-btn(type="button" data-target="mcpJsonStdio")
44
+ i.bi.bi-clipboard.me-1
45
+ | Copy
46
+ p.small.text-muted.mb-0.mt-3
47
+ | HTTP endpoint:
48
+ code= mcp_url
49
+ | ·
50
+ a(href="/docs/md/mcp.md") Full MCP docs
51
+
52
+ h2.h5.mb-3 Try it here
53
+ p.text-muted.mb-3.small
54
+ | Same read-only tools and permissions as
55
+ code GET /api
56
+ | . Commands below call
57
+ code POST /docs/mcp/call
58
+ | with your signed-in key.
59
+
60
+ .mcp-chat-wrap
61
+ .mcp-chat-thread#mcpChatThread(role="log" aria-live="polite" aria-label="MCP conversation")
62
+ .mcp-msg.mcp-msg-bot
63
+ .mcp-msg-label Assistant
64
+ .mcp-msg-body
65
+ p.mb-2 Ask about your data — or try:
66
+ ul.small.mb-0
67
+ li
68
+ code list models
69
+ li
70
+ code describe MODEL
71
+ li
72
+ code count MODEL
73
+ li
74
+ code find MODEL limit=5
75
+ li
76
+ code help
77
+
78
+ .mcp-chat-input-wrap
79
+ .d-flex.flex-wrap.gap-2.mb-2
80
+ each action in [{label: 'List models', cmd: 'list models'}, {label: 'Help', cmd: 'help'}]
81
+ button.btn.btn-sm.btn-outline-secondary.mcp-quick-btn(type="button" data-cmd=action.cmd)= action.label
82
+ form#mcpChatForm
83
+ .input-group
84
+ input#mcpChatInput.form-control(type="text" placeholder="Ask about your data…" autocomplete="off" aria-label="MCP message")
85
+ button.btn.btn-primary(type="submit")
86
+ i.bi.bi-send-fill.me-1
87
+ | Send
88
+
89
+ details.mt-4
90
+ summary.fw-semibold Advanced — call a tool directly
91
+ p.small.text-muted.mt-2 Pick a tool and pass JSON arguments (same as Cursor/LM Studio MCP clients).
92
+ .mcp-tool-panel.mt-2
93
+ .row.g-2.mb-2
94
+ .col-md-4
95
+ label.form-label.small.fw-semibold Tool
96
+ select#mcpToolSelect.form-select.form-select-sm
97
+ option(value="jxp_list_models") jxp_list_models
98
+ option(value="jxp_describe_model") jxp_describe_model
99
+ option(value="jxp_find") jxp_find
100
+ option(value="jxp_count") jxp_count
101
+ option(value="jxp_export_csv") jxp_export_csv
102
+ .col-md-8
103
+ label.form-label.small.fw-semibold Arguments (JSON)
104
+ textarea#mcpToolArgs.form-control.form-control-sm.font-monospace(rows="4") {}
105
+ button.btn.btn-sm.btn-outline-primary#mcpToolRun(type="button")
106
+ i.bi.bi-play-fill.me-1
107
+ | Run tool
108
+
109
+ block javascripts
110
+ script.
111
+ window.__MCP_SETUP__ = !{JSON.stringify({ mcpUrl: mcp_url || '', baseUrl: base_url || '', mcpPath: mcp_path || '/mcp', serverSlug: mcp_server_slug || 'jxp' })};
112
+ script(src="/docs/assets/mcp-console.js" defer)
@@ -7,6 +7,8 @@ header.docs-topbar.navbar.navbar-expand-lg.sticky-top
7
7
  .ms-auto.d-flex.align-items-center.gap-2.flex-wrap
8
8
  if docs_access === 'protected' && !docs_authenticated
9
9
  a.btn.btn-sm.btn-outline-primary(href="/docs/login?next=/docs/api") Explore API
10
+ if mcp_enabled
11
+ a.btn.btn-sm.btn-outline-primary(href="/docs/login?next=/docs/mcp") Ask MCP
10
12
  if docs_access !== 'disabled' && (docs_access !== 'protected' || docs_authenticated)
11
13
  .docs-api-key-wrap
12
14
  label.visually-hidden(for="docs-api-key") API Key
@@ -7,6 +7,10 @@ nav.docs-sidebar#docsSidebarDesktop(aria-label="Documentation navigation")
7
7
  a.docs-nav-link(href="/docs/md/" + file class=(active_guide === file ? 'active' : ''))= label
8
8
  .docs-sidebar-section
9
9
  .docs-sidebar-title Operations
10
+ if mcp_enabled
11
+ a.docs-nav-link(href="/docs/mcp" class=(active_section === 'mcp' ? 'active' : ''))
12
+ i.bi.bi-robot.me-1
13
+ | MCP assistant
10
14
  a.docs-nav-link(href="/docs/diagnostics" class=(active_section === 'diagnostics' ? 'active' : ''))
11
15
  i.bi.bi-speedometer2.me-1
12
16
  | Index diagnostics