jxp 4.2.0 → 5.0.0
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/.env.sample +16 -0
- package/README.md +2 -0
- package/dist/bin/mcp-stdio-bridge.d.ts +3 -0
- package/dist/bin/mcp-stdio-bridge.d.ts.map +1 -0
- package/dist/bin/mcp-stdio-bridge.js +87 -0
- package/dist/bin/mcp-stdio-bridge.js.map +1 -0
- package/dist/bin/server.d.ts.map +1 -1
- package/dist/bin/server.js +2 -1
- package/dist/bin/server.js.map +1 -1
- package/dist/libs/docs-auth.d.ts.map +1 -1
- package/dist/libs/docs-auth.js +4 -2
- package/dist/libs/docs-auth.js.map +1 -1
- package/dist/libs/docs.js +73 -42
- package/dist/libs/docs.js.map +1 -1
- package/dist/libs/jxp.d.ts.map +1 -1
- package/dist/libs/jxp.js +16 -319
- package/dist/libs/jxp.js.map +1 -1
- package/dist/libs/mcp/auth.d.ts +4 -0
- package/dist/libs/mcp/auth.d.ts.map +1 -0
- package/dist/libs/mcp/auth.js +49 -0
- package/dist/libs/mcp/auth.js.map +1 -0
- package/dist/libs/mcp/config.d.ts +21 -0
- package/dist/libs/mcp/config.d.ts.map +1 -0
- package/dist/libs/mcp/config.js +66 -0
- package/dist/libs/mcp/config.js.map +1 -0
- package/dist/libs/mcp/context.d.ts +5 -0
- package/dist/libs/mcp/context.d.ts.map +1 -0
- package/dist/libs/mcp/context.js +14 -0
- package/dist/libs/mcp/context.js.map +1 -0
- package/dist/libs/mcp/describe.d.ts +13 -0
- package/dist/libs/mcp/describe.d.ts.map +1 -0
- package/dist/libs/mcp/describe.js +36 -0
- package/dist/libs/mcp/describe.js.map +1 -0
- package/dist/libs/mcp/docs_auth.d.ts +5 -0
- package/dist/libs/mcp/docs_auth.d.ts.map +1 -0
- package/dist/libs/mcp/docs_auth.js +36 -0
- package/dist/libs/mcp/docs_auth.js.map +1 -0
- package/dist/libs/mcp/dynamic_import.d.ts +6 -0
- package/dist/libs/mcp/dynamic_import.d.ts.map +1 -0
- package/dist/libs/mcp/dynamic_import.js +12 -0
- package/dist/libs/mcp/dynamic_import.js.map +1 -0
- package/dist/libs/mcp/execute_tool.d.ts +10 -0
- package/dist/libs/mcp/execute_tool.d.ts.map +1 -0
- package/dist/libs/mcp/execute_tool.js +65 -0
- package/dist/libs/mcp/execute_tool.js.map +1 -0
- package/dist/libs/mcp/guides/default.md +67 -0
- package/dist/libs/mcp/guides.d.ts +14 -0
- package/dist/libs/mcp/guides.d.ts.map +1 -0
- package/dist/libs/mcp/guides.js +127 -0
- package/dist/libs/mcp/guides.js.map +1 -0
- package/dist/libs/mcp/model_visibility.d.ts +31 -0
- package/dist/libs/mcp/model_visibility.d.ts.map +1 -0
- package/dist/libs/mcp/model_visibility.js +73 -0
- package/dist/libs/mcp/model_visibility.js.map +1 -0
- package/dist/libs/mcp/mount.d.ts +10 -0
- package/dist/libs/mcp/mount.d.ts.map +1 -0
- package/dist/libs/mcp/mount.js +68 -0
- package/dist/libs/mcp/mount.js.map +1 -0
- package/dist/libs/mcp/read_service.d.ts +23 -0
- package/dist/libs/mcp/read_service.d.ts.map +1 -0
- package/dist/libs/mcp/read_service.js +141 -0
- package/dist/libs/mcp/read_service.js.map +1 -0
- package/dist/libs/mcp/server.d.ts +23 -0
- package/dist/libs/mcp/server.d.ts.map +1 -0
- package/dist/libs/mcp/server.js +111 -0
- package/dist/libs/mcp/server.js.map +1 -0
- package/dist/libs/read_handlers.d.ts +13 -0
- package/dist/libs/read_handlers.d.ts.map +1 -0
- package/dist/libs/read_handlers.js +381 -0
- package/dist/libs/read_handlers.js.map +1 -0
- package/dist/libs/schema_serialize.d.ts +3 -0
- package/dist/libs/schema_serialize.d.ts.map +1 -0
- package/dist/libs/schema_serialize.js +45 -0
- package/dist/libs/schema_serialize.js.map +1 -0
- package/dist/types/jxp-config.d.ts +7 -0
- package/dist/types/jxp-config.d.ts.map +1 -1
- package/docs/changelog.md +35 -0
- package/docs/index.md +1 -0
- package/docs/mcp.md +134 -0
- package/mkdocs.yml +1 -0
- package/package.json +11 -5
- package/templates/assets/docs.css +107 -0
- package/templates/assets/mcp-console.js +343 -0
- package/templates/index.pug +37 -0
- package/templates/mcp.pug +112 -0
- package/templates/navbar.pug +2 -0
- package/templates/sidebar.pug +4 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/libs/mcp/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAyBzD,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,eAAe,EAAE,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAsBlH"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.authenticateMcpRequest = authenticateMcpRequest;
|
|
4
|
+
const errors = require("restify-errors");
|
|
5
|
+
const security = require("../security");
|
|
6
|
+
function headerValue(req, name) {
|
|
7
|
+
const raw = req.headers[name.toLowerCase()];
|
|
8
|
+
if (Array.isArray(raw))
|
|
9
|
+
return raw[0];
|
|
10
|
+
return raw;
|
|
11
|
+
}
|
|
12
|
+
function parseApiKey(req) {
|
|
13
|
+
const fromHeader = headerValue(req, "x-api-key");
|
|
14
|
+
if (fromHeader)
|
|
15
|
+
return fromHeader;
|
|
16
|
+
const auth = headerValue(req, "authorization");
|
|
17
|
+
if (auth?.trim().toLowerCase().startsWith("bearer ")) {
|
|
18
|
+
return auth.trim().slice(7);
|
|
19
|
+
}
|
|
20
|
+
const url = req.url || "";
|
|
21
|
+
const q = url.includes("?") ? url.slice(url.indexOf("?") + 1) : "";
|
|
22
|
+
const params = new URLSearchParams(q);
|
|
23
|
+
const fromQuery = params.get("apikey");
|
|
24
|
+
return fromQuery || undefined;
|
|
25
|
+
}
|
|
26
|
+
async function authenticateMcpRequest(req, requireApiKey) {
|
|
27
|
+
const apiKey = parseApiKey(req);
|
|
28
|
+
if (!apiKey) {
|
|
29
|
+
if (requireApiKey) {
|
|
30
|
+
throw new errors.UnauthorizedError("MCP requires an API key (X-API-Key header or Bearer token)");
|
|
31
|
+
}
|
|
32
|
+
return { user: null, groups: [] };
|
|
33
|
+
}
|
|
34
|
+
let user = null;
|
|
35
|
+
try {
|
|
36
|
+
user = await security.apiKeyAuth(apiKey);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
try {
|
|
40
|
+
user = await security.bearerAuth(apiKey);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new errors.UnauthorizedError("Invalid API key or token");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const groups = await security.getGroups(user._id);
|
|
47
|
+
return { user, groups };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/libs/mcp/auth.ts"],"names":[],"mappings":";;AA0BA,wDAsBC;AA7CD,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;AACzC,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AAExC,SAAS,WAAW,CAAC,GAAoB,EAAE,IAAY;IACtD,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;IACtC,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,SAAS,WAAW,CAAC,GAAoB;IACxC,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACjD,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAClC,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC/C,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACtD,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC;IAC1B,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvC,OAAO,SAAS,IAAI,SAAS,CAAC;AAC/B,CAAC;AAEM,KAAK,UAAU,sBAAsB,CAAC,GAAoB,EAAE,aAAsB;IACxF,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,IAAI,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,MAAM,CAAC,iBAAiB,CAAC,4DAA4D,CAAC,CAAC;QAClG,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACnC,CAAC;IAED,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,IAAI,CAAC;QACJ,IAAI,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACR,IAAI,CAAC;YACJ,IAAI,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACR,MAAM,IAAI,MAAM,CAAC,iBAAiB,CAAC,0BAA0B,CAAC,CAAC;QAChE,CAAC;IACF,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { JXPQueryLimits } from "../../types/jxp-config";
|
|
2
|
+
export interface McpConfig {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
path: string;
|
|
5
|
+
requireApiKey: boolean;
|
|
6
|
+
modelWhitelist: Set<string>;
|
|
7
|
+
modelBlacklist: Set<string>;
|
|
8
|
+
defaultHiddenModels: Set<string>;
|
|
9
|
+
defaultLimit: number;
|
|
10
|
+
maxLimit: number;
|
|
11
|
+
maxResponseBytes: number;
|
|
12
|
+
maxCsvBytes: number;
|
|
13
|
+
truncateStringsAt: number;
|
|
14
|
+
disableAutopopulate: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function getMcpConfig(): McpConfig;
|
|
17
|
+
export declare function isMcpEnabled(): boolean;
|
|
18
|
+
/** Merge MCP-specific limits over the app's query_limits for MCP read operations. */
|
|
19
|
+
export declare function overlayMcpQueryLimits(base: JXPQueryLimits | undefined, mcp: McpConfig): JXPQueryLimits;
|
|
20
|
+
export declare function overlayMcpCsvLimits(base: JXPQueryLimits | undefined, mcp: McpConfig): JXPQueryLimits;
|
|
21
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/libs/mcp/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,MAAM,WAAW,SAAS;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,OAAO,CAAC;CAC7B;AA2BD,wBAAgB,YAAY,IAAI,SAAS,CAsBxC;AAED,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED,qFAAqF;AACrF,wBAAgB,qBAAqB,CACpC,IAAI,EAAE,cAAc,GAAG,SAAS,EAChC,GAAG,EAAE,SAAS,GACZ,cAAc,CAMhB;AAED,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,cAAc,GAAG,SAAS,EAChC,GAAG,EAAE,SAAS,GACZ,cAAc,CAIhB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getMcpConfig = getMcpConfig;
|
|
4
|
+
exports.isMcpEnabled = isMcpEnabled;
|
|
5
|
+
exports.overlayMcpQueryLimits = overlayMcpQueryLimits;
|
|
6
|
+
exports.overlayMcpCsvLimits = overlayMcpCsvLimits;
|
|
7
|
+
const load_config_1 = require("../load-config");
|
|
8
|
+
const parse_byte_size_1 = require("../parse_byte_size");
|
|
9
|
+
const builtin_models_1 = require("../builtin_models");
|
|
10
|
+
function envBool(name, fallback) {
|
|
11
|
+
(0, load_config_1.loadEnv)();
|
|
12
|
+
const v = process.env[name];
|
|
13
|
+
if (v === undefined)
|
|
14
|
+
return fallback;
|
|
15
|
+
return v === "1" || v.toLowerCase() === "true";
|
|
16
|
+
}
|
|
17
|
+
function envInt(name, fallback) {
|
|
18
|
+
(0, load_config_1.loadEnv)();
|
|
19
|
+
const n = parseInt(process.env[name] ?? "", 10);
|
|
20
|
+
return Number.isFinite(n) ? n : fallback;
|
|
21
|
+
}
|
|
22
|
+
function parseSlugSet(raw) {
|
|
23
|
+
if (!raw?.trim())
|
|
24
|
+
return new Set();
|
|
25
|
+
return new Set(raw
|
|
26
|
+
.split(",")
|
|
27
|
+
.map((s) => s.trim().toLowerCase())
|
|
28
|
+
.filter(Boolean));
|
|
29
|
+
}
|
|
30
|
+
const DEFAULT_HIDDEN = [...builtin_models_1.BUILTIN_MODEL_SLUGS];
|
|
31
|
+
function getMcpConfig() {
|
|
32
|
+
(0, load_config_1.loadEnv)();
|
|
33
|
+
return {
|
|
34
|
+
enabled: envBool("MCP_ENABLED", false),
|
|
35
|
+
path: process.env.MCP_PATH?.trim() || "/mcp",
|
|
36
|
+
requireApiKey: envBool("MCP_REQUIRE_API_KEY", true),
|
|
37
|
+
modelWhitelist: parseSlugSet(process.env.MCP_MODEL_WHITELIST),
|
|
38
|
+
modelBlacklist: parseSlugSet(process.env.MCP_MODEL_BLACKLIST),
|
|
39
|
+
defaultHiddenModels: process.env.MCP_DEFAULT_HIDDEN_MODELS?.trim()
|
|
40
|
+
? parseSlugSet(process.env.MCP_DEFAULT_HIDDEN_MODELS)
|
|
41
|
+
: new Set(DEFAULT_HIDDEN),
|
|
42
|
+
defaultLimit: envInt("MCP_DEFAULT_LIMIT", 20),
|
|
43
|
+
maxLimit: envInt("MCP_MAX_LIMIT", 100),
|
|
44
|
+
maxResponseBytes: (0, parse_byte_size_1.parseByteSizeOr)(process.env.MCP_MAX_RESPONSE_SIZE, "256kb", "MCP_MAX_RESPONSE_SIZE"),
|
|
45
|
+
maxCsvBytes: (0, parse_byte_size_1.parseByteSizeOr)(process.env.MCP_MAX_CSV_SIZE, "512kb", "MCP_MAX_CSV_SIZE"),
|
|
46
|
+
truncateStringsAt: envInt("MCP_TRUNCATE_STRINGS_AT", 4000),
|
|
47
|
+
disableAutopopulate: envBool("MCP_DISABLE_AUTOPOPULATE", true),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function isMcpEnabled() {
|
|
51
|
+
return getMcpConfig().enabled;
|
|
52
|
+
}
|
|
53
|
+
/** Merge MCP-specific limits over the app's query_limits for MCP read operations. */
|
|
54
|
+
function overlayMcpQueryLimits(base, mcp) {
|
|
55
|
+
const merged = { ...(base || {}) };
|
|
56
|
+
merged.default = mcp.defaultLimit;
|
|
57
|
+
merged.max = mcp.maxLimit;
|
|
58
|
+
merged.max_response_size = mcp.maxResponseBytes;
|
|
59
|
+
return merged;
|
|
60
|
+
}
|
|
61
|
+
function overlayMcpCsvLimits(base, mcp) {
|
|
62
|
+
const merged = overlayMcpQueryLimits(base, mcp);
|
|
63
|
+
merged.max_response_size = mcp.maxCsvBytes;
|
|
64
|
+
return merged;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/libs/mcp/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import type { McpAuthContext } from "./model_visibility";
|
|
3
|
+
export declare const mcpAuthStorage: AsyncLocalStorage<McpAuthContext>;
|
|
4
|
+
export declare function getMcpAuth(): McpAuthContext;
|
|
5
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/libs/mcp/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,eAAO,MAAM,cAAc,mCAA0C,CAAC;AAEtE,wBAAgB,UAAU,IAAI,cAAc,CAM3C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mcpAuthStorage = void 0;
|
|
4
|
+
exports.getMcpAuth = getMcpAuth;
|
|
5
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
6
|
+
exports.mcpAuthStorage = new node_async_hooks_1.AsyncLocalStorage();
|
|
7
|
+
function getMcpAuth() {
|
|
8
|
+
const ctx = exports.mcpAuthStorage.getStore();
|
|
9
|
+
if (!ctx) {
|
|
10
|
+
throw new Error("MCP auth context missing");
|
|
11
|
+
}
|
|
12
|
+
return ctx;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/libs/mcp/context.ts"],"names":[],"mappings":";;;AAKA,gCAMC;AAXD,uDAAqD;AAGxC,QAAA,cAAc,GAAG,IAAI,oCAAiB,EAAkB,CAAC;AAEtE,SAAgB,UAAU;IACzB,MAAM,GAAG,GAAG,sBAAc,CAAC,QAAQ,EAAE,CAAC;IACtC,IAAI,CAAC,GAAG,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Model } from "mongoose";
|
|
2
|
+
import { serializeField } from "../schema_serialize";
|
|
3
|
+
export interface ModelDescription {
|
|
4
|
+
slug: string;
|
|
5
|
+
collection: string;
|
|
6
|
+
soft_delete: boolean;
|
|
7
|
+
fields: Record<string, ReturnType<typeof serializeField>>;
|
|
8
|
+
populate_hints: string[];
|
|
9
|
+
filterable_fields: string[];
|
|
10
|
+
notes: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function describeModelJson(slug: string, model: Model<unknown>): ModelDescription;
|
|
13
|
+
//# sourceMappingURL=describe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../../../src/libs/mcp/describe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC;IAC1D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,gBAAgB,CAkCvF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.describeModelJson = describeModelJson;
|
|
4
|
+
const schema_serialize_1 = require("../schema_serialize");
|
|
5
|
+
function describeModelJson(slug, model) {
|
|
6
|
+
const fields = Object.keys(model.schema.paths).sort();
|
|
7
|
+
const safeFields = {};
|
|
8
|
+
const populateHints = [];
|
|
9
|
+
const filterableFields = [];
|
|
10
|
+
for (const fieldName of fields) {
|
|
11
|
+
const path = model.schema.paths[fieldName];
|
|
12
|
+
safeFields[fieldName] = (0, schema_serialize_1.serializeField)(path);
|
|
13
|
+
if (path.options?.index) {
|
|
14
|
+
filterableFields.push(fieldName);
|
|
15
|
+
}
|
|
16
|
+
if (path.instance === "ObjectID" && path.options?.link) {
|
|
17
|
+
const populateKey = String(path.options.map_to || path.options.virtual || path.options.link.toLowerCase());
|
|
18
|
+
populateHints.push(`populate[${fieldName}]=<fields>`);
|
|
19
|
+
populateHints.push(`populate[${populateKey}]=<fields>`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
slug,
|
|
24
|
+
collection: model.collection.name,
|
|
25
|
+
soft_delete: true,
|
|
26
|
+
fields: safeFields,
|
|
27
|
+
populate_hints: [...new Set(populateHints)],
|
|
28
|
+
filterable_fields: filterableFields,
|
|
29
|
+
notes: [
|
|
30
|
+
"Use jxp_find with filter objects ($gte:, $regex:/pattern/i, etc.).",
|
|
31
|
+
"Omit deleted documents by default; not applicable for single-id fetch of deleted rows.",
|
|
32
|
+
"Prefer fields and low limit to keep responses small.",
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=describe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../../../src/libs/mcp/describe.ts"],"names":[],"mappings":";;AAaA,8CAkCC;AA9CD,0DAAqD;AAYrD,SAAgB,iBAAiB,CAAC,IAAY,EAAE,KAAqB;IACpE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,MAAM,UAAU,GAAsD,EAAE,CAAC;IACzE,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,MAAM,gBAAgB,GAAa,EAAE,CAAC;IAEtC,KAAK,MAAM,SAAS,IAAI,MAAM,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC3C,UAAU,CAAC,SAAS,CAAC,GAAG,IAAA,iCAAc,EAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;YACzB,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;YACxD,MAAM,WAAW,GAAG,MAAM,CACzB,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAC9E,CAAC;YACF,aAAa,CAAC,IAAI,CAAC,YAAY,SAAS,YAAY,CAAC,CAAC;YACtD,aAAa,CAAC,IAAI,CAAC,YAAY,WAAW,YAAY,CAAC,CAAC;QACzD,CAAC;IACF,CAAC;IAED,OAAO;QACN,IAAI;QACJ,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI;QACjC,WAAW,EAAE,IAAI;QACjB,MAAM,EAAE,UAAU;QAClB,cAAc,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;QAC3C,iBAAiB,EAAE,gBAAgB;QACnC,KAAK,EAAE;YACN,oEAAoE;YACpE,wFAAwF;YACxF,sDAAsD;SACtD;KACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { JXPRequest } from "../../types/jxp-config";
|
|
2
|
+
import type { McpAuthContext } from "./model_visibility";
|
|
3
|
+
/** Resolve MCP auth for docs playground: navbar key, then HttpOnly session. */
|
|
4
|
+
export declare function authenticateDocsMcpRequest(req: JXPRequest): Promise<McpAuthContext>;
|
|
5
|
+
//# sourceMappingURL=docs_auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs_auth.d.ts","sourceRoot":"","sources":["../../../src/libs/mcp/docs_auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AASzD,+EAA+E;AAC/E,wBAAsB,0BAA0B,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,cAAc,CAAC,CAmBzF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.authenticateDocsMcpRequest = authenticateDocsMcpRequest;
|
|
7
|
+
const restify_errors_1 = __importDefault(require("restify-errors"));
|
|
8
|
+
const docs_auth_1 = require("../docs-auth");
|
|
9
|
+
const auth_1 = require("./auth");
|
|
10
|
+
const config_1 = require("./config");
|
|
11
|
+
function headerApiKey(req) {
|
|
12
|
+
const raw = req.headers["x-api-key"];
|
|
13
|
+
if (Array.isArray(raw))
|
|
14
|
+
return raw[0]?.trim() || undefined;
|
|
15
|
+
return typeof raw === "string" && raw.trim() ? raw.trim() : undefined;
|
|
16
|
+
}
|
|
17
|
+
/** Resolve MCP auth for docs playground: navbar key, then HttpOnly session. */
|
|
18
|
+
async function authenticateDocsMcpRequest(req) {
|
|
19
|
+
const mcpConfig = (0, config_1.getMcpConfig)();
|
|
20
|
+
const fromHeader = headerApiKey(req);
|
|
21
|
+
if (fromHeader) {
|
|
22
|
+
req.headers["x-api-key"] = fromHeader;
|
|
23
|
+
return (0, auth_1.authenticateMcpRequest)(req, mcpConfig.requireApiKey);
|
|
24
|
+
}
|
|
25
|
+
const session = req.docsSession
|
|
26
|
+
?? (0, docs_auth_1.verifyDocsSession)(req);
|
|
27
|
+
if (session?.apikey) {
|
|
28
|
+
req.headers["x-api-key"] = session.apikey;
|
|
29
|
+
return (0, auth_1.authenticateMcpRequest)(req, mcpConfig.requireApiKey);
|
|
30
|
+
}
|
|
31
|
+
if (mcpConfig.requireApiKey) {
|
|
32
|
+
throw new restify_errors_1.default.UnauthorizedError("API key required (top bar or sign in)");
|
|
33
|
+
}
|
|
34
|
+
return (0, auth_1.authenticateMcpRequest)(req, false);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=docs_auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs_auth.js","sourceRoot":"","sources":["../../../src/libs/mcp/docs_auth.ts"],"names":[],"mappings":";;;;;AAcA,gEAmBC;AAjCD,oEAAoC;AAEpC,4CAAiD;AACjD,iCAAgD;AAEhD,qCAAwC;AAExC,SAAS,YAAY,CAAC,GAAe;IACpC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IAC3D,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC;AAED,+EAA+E;AACxE,KAAK,UAAU,0BAA0B,CAAC,GAAe;IAC/D,MAAM,SAAS,GAAG,IAAA,qBAAY,GAAE,CAAC;IACjC,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,UAAU,EAAE,CAAC;QAChB,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,UAAU,CAAC;QACtC,OAAO,IAAA,6BAAsB,EAAC,GAAqD,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;IAC/G,CAAC;IAED,MAAM,OAAO,GAAI,GAAyD,CAAC,WAAW;WAClF,IAAA,6BAAiB,EAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;QACrB,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;QAC1C,OAAO,IAAA,6BAAsB,EAAC,GAAqD,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;IAC/G,CAAC;IAED,IAAI,SAAS,CAAC,aAAa,EAAE,CAAC;QAC7B,MAAM,IAAI,wBAAM,CAAC,iBAAiB,CAAC,uCAAuC,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,IAAA,6BAAsB,EAAC,GAAqD,EAAE,KAAK,CAAC,CAAC;AAC7F,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load ESM-only packages from CommonJS output without TypeScript rewriting
|
|
3
|
+
* `import()` to `require()` (which breaks @modelcontextprotocol/* v2).
|
|
4
|
+
*/
|
|
5
|
+
export declare function dynamicImport<T = Record<string, unknown>>(specifier: string): Promise<T>;
|
|
6
|
+
//# sourceMappingURL=dynamic_import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamic_import.d.ts","sourceRoot":"","sources":["../../../src/libs/mcp/dynamic_import.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAGxF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dynamicImport = dynamicImport;
|
|
4
|
+
/**
|
|
5
|
+
* Load ESM-only packages from CommonJS output without TypeScript rewriting
|
|
6
|
+
* `import()` to `require()` (which breaks @modelcontextprotocol/* v2).
|
|
7
|
+
*/
|
|
8
|
+
function dynamicImport(specifier) {
|
|
9
|
+
const fn = new Function("specifier", "return import(specifier)");
|
|
10
|
+
return fn(specifier);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=dynamic_import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamic_import.js","sourceRoot":"","sources":["../../../src/libs/mcp/dynamic_import.ts"],"names":[],"mappings":";;AAIA,sCAGC;AAPD;;;GAGG;AACH,SAAgB,aAAa,CAA8B,SAAiB;IAC3E,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAA8B,CAAC;IAC9F,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { JXPConfig } from "../../types/jxp-config";
|
|
2
|
+
import type { ModelRegistry } from "../builtin_models";
|
|
3
|
+
import { type McpAuthContext } from "./model_visibility";
|
|
4
|
+
export type McpToolResult = {
|
|
5
|
+
text: string;
|
|
6
|
+
isError?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare function isAllowedMcpTool(name: string): boolean;
|
|
9
|
+
export declare function executeMcpTool(name: string, args: Record<string, unknown>, config: JXPConfig, models: ModelRegistry, auth: McpAuthContext): Promise<McpToolResult>;
|
|
10
|
+
//# sourceMappingURL=execute_tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute_tool.d.ts","sourceRoot":"","sources":["../../../src/libs/mcp/execute_tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,OAAO,EAA4C,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGnG,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAUhE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,wBAAsB,cAAc,CACnC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,cAAc,GAClB,OAAO,CAAC,aAAa,CAAC,CAyDxB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isAllowedMcpTool = isAllowedMcpTool;
|
|
4
|
+
exports.executeMcpTool = executeMcpTool;
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
const context_1 = require("./context");
|
|
7
|
+
const describe_1 = require("./describe");
|
|
8
|
+
const model_visibility_1 = require("./model_visibility");
|
|
9
|
+
const read_service_1 = require("./read_service");
|
|
10
|
+
const ALLOWED_TOOLS = new Set([
|
|
11
|
+
"jxp_list_models",
|
|
12
|
+
"jxp_describe_model",
|
|
13
|
+
"jxp_find",
|
|
14
|
+
"jxp_count",
|
|
15
|
+
"jxp_export_csv",
|
|
16
|
+
]);
|
|
17
|
+
function isAllowedMcpTool(name) {
|
|
18
|
+
return ALLOWED_TOOLS.has(name);
|
|
19
|
+
}
|
|
20
|
+
async function executeMcpTool(name, args, config, models, auth) {
|
|
21
|
+
if (!isAllowedMcpTool(name)) {
|
|
22
|
+
return { text: `Unknown MCP tool: ${name}`, isError: true };
|
|
23
|
+
}
|
|
24
|
+
const mcpConfig = (0, config_1.getMcpConfig)();
|
|
25
|
+
try {
|
|
26
|
+
return await context_1.mcpAuthStorage.run(auth, async () => {
|
|
27
|
+
switch (name) {
|
|
28
|
+
case "jxp_list_models": {
|
|
29
|
+
const list = await (0, model_visibility_1.listVisibleModels)(models, auth, mcpConfig);
|
|
30
|
+
return { text: JSON.stringify(list, null, 2) };
|
|
31
|
+
}
|
|
32
|
+
case "jxp_describe_model": {
|
|
33
|
+
const slug = String(args.model ?? "");
|
|
34
|
+
if (!slug)
|
|
35
|
+
throw new Error("model is required");
|
|
36
|
+
const Model = models[slug];
|
|
37
|
+
if (!Model)
|
|
38
|
+
throw new Error(`Model "${slug}" not found`);
|
|
39
|
+
await (0, model_visibility_1.assertModelVisibleMcp)(slug, Model, auth, mcpConfig);
|
|
40
|
+
return { text: JSON.stringify((0, describe_1.describeModelJson)(slug, Model), null, 2) };
|
|
41
|
+
}
|
|
42
|
+
case "jxp_find": {
|
|
43
|
+
const result = await (0, read_service_1.mcpFind)(models, config, auth, args);
|
|
44
|
+
return {
|
|
45
|
+
text: typeof result === "string" ? result : JSON.stringify(result, null, 2),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
case "jxp_count": {
|
|
49
|
+
const result = await (0, read_service_1.mcpCount)(models, config, auth, args);
|
|
50
|
+
return { text: JSON.stringify(result, null, 2) };
|
|
51
|
+
}
|
|
52
|
+
case "jxp_export_csv": {
|
|
53
|
+
const csv = await (0, read_service_1.mcpExportCsv)(models, config, auth, args);
|
|
54
|
+
return { text: csv };
|
|
55
|
+
}
|
|
56
|
+
default:
|
|
57
|
+
return { text: `Unknown MCP tool: ${name}`, isError: true };
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
return { text: err instanceof Error ? err.message : String(err), isError: true };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=execute_tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute_tool.js","sourceRoot":"","sources":["../../../src/libs/mcp/execute_tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# JXP MCP usage guide
|
|
2
|
+
|
|
3
|
+
Read-only access to JXP REST data via five fixed tools. Permissions match the authenticated user's API key.
|
|
4
|
+
|
|
5
|
+
## Workflow
|
|
6
|
+
|
|
7
|
+
1. **`jxp_list_models`** — discover model slugs available to this caller.
|
|
8
|
+
2. **`jxp_describe_model`** — field types, indexed/filterable fields, link/populate hints.
|
|
9
|
+
3. **`jxp_count`** — check result size before fetching (especially large collections).
|
|
10
|
+
4. **`jxp_find`** — list or get by `id`; always set `fields` and a low `limit`.
|
|
11
|
+
5. **`jxp_export_csv`** — small CSV exports when JSON is awkward; no populate.
|
|
12
|
+
|
|
13
|
+
Never guess model names. Never fetch large collections without `fields` and `limit`.
|
|
14
|
+
|
|
15
|
+
## Query parameters (jxp_find / jxp_count / jxp_export_csv)
|
|
16
|
+
|
|
17
|
+
### filter
|
|
18
|
+
|
|
19
|
+
Mongo-style filter object. Examples:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{ "filter": { "email": "user@example.com" } }
|
|
23
|
+
{ "filter": { "created": { "$gte": "2024-01-01" } } }
|
|
24
|
+
{ "filter": { "status": { "$in": ["active", "trial"] } } }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Use `jxp_describe_model` to see indexed fields worth filtering on.
|
|
28
|
+
|
|
29
|
+
### search
|
|
30
|
+
|
|
31
|
+
Full-text or field search (same as REST `?search=`).
|
|
32
|
+
|
|
33
|
+
### populate
|
|
34
|
+
|
|
35
|
+
Link expansion: `"populate[user]=name,email"` or object `{ "user": "name,email" }`.
|
|
36
|
+
Autopopulate is disabled for MCP by default (`MCP_DISABLE_AUTOPOPULATE=true`).
|
|
37
|
+
|
|
38
|
+
### fields
|
|
39
|
+
|
|
40
|
+
Comma-separated projection — **always use** for list queries:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{ "model": "reader", "fields": "_id,email,name", "limit": 10 }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### sort, limit, page, skip
|
|
47
|
+
|
|
48
|
+
- `limit` — defaults to MCP default; hard-capped at MCP max.
|
|
49
|
+
- `page` / `skip` — pagination.
|
|
50
|
+
- `sort` — e.g. `-created` for newest first.
|
|
51
|
+
|
|
52
|
+
## Limits and safety
|
|
53
|
+
|
|
54
|
+
- MCP enforces stricter limits than HTTP (`MCP_DEFAULT_LIMIT`, `MCP_MAX_LIMIT`).
|
|
55
|
+
- JSON responses are capped (`MCP_MAX_RESPONSE_SIZE`); long strings are truncated.
|
|
56
|
+
- CSV exports are capped (`MCP_MAX_CSV_SIZE`).
|
|
57
|
+
- **Read-only** — no create, update, delete, aggregate, or stored procedures via MCP.
|
|
58
|
+
|
|
59
|
+
## Hidden models
|
|
60
|
+
|
|
61
|
+
Built-in auth collections (`user`, `apikey`, `token`, etc.) and `internal: true` schemas are hidden unless whitelisted via `MCP_MODEL_WHITELIST`. Blacklist always wins.
|
|
62
|
+
|
|
63
|
+
## Single document by id
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{ "model": "reader", "id": "507f1f77bcf86cd799439011" }
|
|
67
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { JXPConfig } from "../../types/jxp-config";
|
|
2
|
+
import type { McpConfig } from "./config";
|
|
3
|
+
export declare const MCP_GUIDE_URI = "jxp://guide";
|
|
4
|
+
export declare const MCP_GUIDE_RESOURCE_NAME = "jxp-guide";
|
|
5
|
+
export interface McpGuideContext {
|
|
6
|
+
config: JXPConfig;
|
|
7
|
+
mcpConfig: McpConfig;
|
|
8
|
+
}
|
|
9
|
+
export declare function loadGuideFiles(paths: string[]): string[];
|
|
10
|
+
export declare function buildMcpGuideMarkdown(ctx: McpGuideContext): string;
|
|
11
|
+
export declare function buildMcpInstructions(ctx: McpGuideContext): string;
|
|
12
|
+
/** Clear cached guide (for tests). */
|
|
13
|
+
export declare function clearMcpGuideCache(): void;
|
|
14
|
+
//# sourceMappingURL=guides.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guides.d.ts","sourceRoot":"","sources":["../../../src/libs/mcp/guides.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAC3C,eAAO,MAAM,uBAAuB,cAAc,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,SAAS,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;CACrB;AA8BD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAkBxD;AAcD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAsBlE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CA8BjE;AAED,sCAAsC;AACtC,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MCP_GUIDE_RESOURCE_NAME = exports.MCP_GUIDE_URI = void 0;
|
|
7
|
+
exports.loadGuideFiles = loadGuideFiles;
|
|
8
|
+
exports.buildMcpGuideMarkdown = buildMcpGuideMarkdown;
|
|
9
|
+
exports.buildMcpInstructions = buildMcpInstructions;
|
|
10
|
+
exports.clearMcpGuideCache = clearMcpGuideCache;
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
exports.MCP_GUIDE_URI = "jxp://guide";
|
|
14
|
+
exports.MCP_GUIDE_RESOURCE_NAME = "jxp-guide";
|
|
15
|
+
let cachedGuideMarkdown = null;
|
|
16
|
+
let cachedGuideKey = null;
|
|
17
|
+
function resolveGuidePath(filePath) {
|
|
18
|
+
if (path_1.default.isAbsolute(filePath))
|
|
19
|
+
return filePath;
|
|
20
|
+
return path_1.default.resolve(process.cwd(), filePath);
|
|
21
|
+
}
|
|
22
|
+
function defaultGuidePath() {
|
|
23
|
+
return path_1.default.join(__dirname, "guides", "default.md");
|
|
24
|
+
}
|
|
25
|
+
function collectGuideFilePaths(config) {
|
|
26
|
+
const paths = [];
|
|
27
|
+
const fromConfig = config.mcp?.guideFiles || [];
|
|
28
|
+
for (const p of fromConfig) {
|
|
29
|
+
if (p?.trim())
|
|
30
|
+
paths.push(p.trim());
|
|
31
|
+
}
|
|
32
|
+
const fromEnv = process.env.MCP_GUIDE_FILES?.trim();
|
|
33
|
+
if (fromEnv) {
|
|
34
|
+
for (const p of fromEnv.split(",")) {
|
|
35
|
+
const t = p.trim();
|
|
36
|
+
if (t)
|
|
37
|
+
paths.push(t);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return paths;
|
|
41
|
+
}
|
|
42
|
+
function loadGuideFiles(paths) {
|
|
43
|
+
const parts = [];
|
|
44
|
+
const defaultPath = defaultGuidePath();
|
|
45
|
+
if (fs_1.default.existsSync(defaultPath)) {
|
|
46
|
+
parts.push(fs_1.default.readFileSync(defaultPath, "utf8"));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
console.warn(new Date(), `MCP default guide not found at ${defaultPath}`);
|
|
50
|
+
}
|
|
51
|
+
for (const filePath of paths) {
|
|
52
|
+
const resolved = resolveGuidePath(filePath);
|
|
53
|
+
try {
|
|
54
|
+
parts.push(fs_1.default.readFileSync(resolved, "utf8"));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
console.warn(new Date(), `MCP guide file not found: ${resolved}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return parts;
|
|
61
|
+
}
|
|
62
|
+
function guideCacheKey(ctx) {
|
|
63
|
+
return JSON.stringify({
|
|
64
|
+
files: collectGuideFilePaths(ctx.config),
|
|
65
|
+
append: process.env.MCP_INSTRUCTIONS_APPEND || "",
|
|
66
|
+
appInstructions: ctx.config.mcp?.instructions || "",
|
|
67
|
+
limits: {
|
|
68
|
+
default: ctx.mcpConfig.defaultLimit,
|
|
69
|
+
max: ctx.mcpConfig.maxLimit,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function buildMcpGuideMarkdown(ctx) {
|
|
74
|
+
const key = guideCacheKey(ctx);
|
|
75
|
+
if (cachedGuideMarkdown !== null && cachedGuideKey === key) {
|
|
76
|
+
return cachedGuideMarkdown;
|
|
77
|
+
}
|
|
78
|
+
const parts = loadGuideFiles(collectGuideFilePaths(ctx.config));
|
|
79
|
+
const runtime = [
|
|
80
|
+
"",
|
|
81
|
+
"## Runtime limits",
|
|
82
|
+
"",
|
|
83
|
+
`- Default \`limit\`: ${ctx.mcpConfig.defaultLimit}`,
|
|
84
|
+
`- Max \`limit\`: ${ctx.mcpConfig.maxLimit}`,
|
|
85
|
+
`- Max JSON response: ${ctx.mcpConfig.maxResponseBytes} bytes`,
|
|
86
|
+
`- Max CSV response: ${ctx.mcpConfig.maxCsvBytes} bytes`,
|
|
87
|
+
`- String truncation at: ${ctx.mcpConfig.truncateStringsAt} characters`,
|
|
88
|
+
"",
|
|
89
|
+
];
|
|
90
|
+
cachedGuideMarkdown = [...parts, runtime.join("\n")].filter(Boolean).join("\n\n---\n\n");
|
|
91
|
+
cachedGuideKey = key;
|
|
92
|
+
return cachedGuideMarkdown;
|
|
93
|
+
}
|
|
94
|
+
function buildMcpInstructions(ctx) {
|
|
95
|
+
const { mcpConfig } = ctx;
|
|
96
|
+
const lines = [
|
|
97
|
+
"You have read-only access to a JXP REST API via MCP tools.",
|
|
98
|
+
"",
|
|
99
|
+
"Required workflow:",
|
|
100
|
+
"1. jxp_list_models — discover available model slugs for this user.",
|
|
101
|
+
"2. jxp_describe_model — inspect fields, filters, and populate hints before querying.",
|
|
102
|
+
"3. jxp_count — estimate result size on large collections before jxp_find.",
|
|
103
|
+
"4. jxp_find — use fields + low limit; pass id for a single document.",
|
|
104
|
+
"5. jxp_export_csv — small tabular exports only (no populate).",
|
|
105
|
+
"",
|
|
106
|
+
`Limits: default limit ${mcpConfig.defaultLimit}, max ${mcpConfig.maxLimit}. Responses are size-capped; strings may be truncated.`,
|
|
107
|
+
"Filter syntax matches MongoDB ($gte, $in, $regex, etc.). Use populate[field]=a,b for links.",
|
|
108
|
+
"Do not mutate data — MCP is read-only.",
|
|
109
|
+
"",
|
|
110
|
+
"For the full guide, read MCP resource jxp-guide (uri jxp://guide).",
|
|
111
|
+
];
|
|
112
|
+
const appInstructions = ctx.config.mcp?.instructions?.trim();
|
|
113
|
+
if (appInstructions) {
|
|
114
|
+
lines.push("", appInstructions);
|
|
115
|
+
}
|
|
116
|
+
const envAppend = process.env.MCP_INSTRUCTIONS_APPEND?.trim();
|
|
117
|
+
if (envAppend) {
|
|
118
|
+
lines.push("", envAppend);
|
|
119
|
+
}
|
|
120
|
+
return lines.join("\n");
|
|
121
|
+
}
|
|
122
|
+
/** Clear cached guide (for tests). */
|
|
123
|
+
function clearMcpGuideCache() {
|
|
124
|
+
cachedGuideMarkdown = null;
|
|
125
|
+
cachedGuideKey = null;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=guides.js.map
|