@yunsoft/yuncms-core 0.1.10 → 0.1.11
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/package.json +1 -1
- package/src/bootstrap.js +2 -0
- package/src/config.js +0 -59
- package/src/migrations/0017-mcp-settings.js +19 -0
package/package.json
CHANGED
package/src/bootstrap.js
CHANGED
|
@@ -16,6 +16,7 @@ import { externalAuthFoundationMigration } from './migrations/0013-external-auth
|
|
|
16
16
|
import { aiSettingsMigration } from './migrations/0014-ai-settings.js';
|
|
17
17
|
import { navigationGroupsMigration } from './migrations/0015-navigation-groups.js';
|
|
18
18
|
import { navigationGroupCollapseMigration } from './migrations/0016-navigation-group-collapse.js';
|
|
19
|
+
import { mcpSettingsMigration } from './migrations/0017-mcp-settings.js';
|
|
19
20
|
import { readSchemaVersion } from './schema-version.js';
|
|
20
21
|
import { ensurePublicRole } from './setup.js';
|
|
21
22
|
|
|
@@ -36,6 +37,7 @@ export const CORE_MIGRATIONS = Object.freeze([
|
|
|
36
37
|
aiSettingsMigration,
|
|
37
38
|
navigationGroupsMigration,
|
|
38
39
|
navigationGroupCollapseMigration,
|
|
40
|
+
mcpSettingsMigration,
|
|
39
41
|
]);
|
|
40
42
|
|
|
41
43
|
export const REQUIRED_CORE_MIGRATION_IDS = Object.freeze(
|
package/src/config.js
CHANGED
|
@@ -38,56 +38,6 @@ function readFailureMode(value, fallback = 'best-effort') {
|
|
|
38
38
|
return mode;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function readOriginList(value, fallback) {
|
|
42
|
-
const entries = readString(value, '')
|
|
43
|
-
.split(',')
|
|
44
|
-
.map((entry) => entry.trim())
|
|
45
|
-
.filter(Boolean);
|
|
46
|
-
const source = entries.length > 0 ? entries : [fallback];
|
|
47
|
-
return [...new Set(source.map((entry) => {
|
|
48
|
-
let url;
|
|
49
|
-
try {
|
|
50
|
-
url = new URL(entry);
|
|
51
|
-
} catch {
|
|
52
|
-
throw new Error(`MCP_ALLOWED_ORIGINS contains an invalid URL: ${entry}`);
|
|
53
|
-
}
|
|
54
|
-
if (!['http:', 'https:'].includes(url.protocol)) {
|
|
55
|
-
throw new Error(`MCP_ALLOWED_ORIGINS must use http or https: ${entry}`);
|
|
56
|
-
}
|
|
57
|
-
return url.origin;
|
|
58
|
-
}))];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function readHostList(value, fallbackOrigin) {
|
|
62
|
-
let fallbackHost;
|
|
63
|
-
try {
|
|
64
|
-
fallbackHost = new URL(fallbackOrigin).host.toLowerCase();
|
|
65
|
-
} catch {
|
|
66
|
-
throw new Error(`Cannot derive MCP host from STUDIO_ORIGIN: ${fallbackOrigin}`);
|
|
67
|
-
}
|
|
68
|
-
const entries = readString(value, '')
|
|
69
|
-
.split(',')
|
|
70
|
-
.map((entry) => entry.trim())
|
|
71
|
-
.filter(Boolean);
|
|
72
|
-
const source = entries.length > 0 ? entries : [fallbackHost];
|
|
73
|
-
|
|
74
|
-
return [...new Set(source.map((entry) => {
|
|
75
|
-
if (/[:][/][/]|[\\/?#@\s\0]/.test(entry)) {
|
|
76
|
-
throw new Error(`MCP_ALLOWED_HOSTS contains an invalid host: ${entry}`);
|
|
77
|
-
}
|
|
78
|
-
let url;
|
|
79
|
-
try {
|
|
80
|
-
url = new URL(`http://${entry}`);
|
|
81
|
-
} catch {
|
|
82
|
-
throw new Error(`MCP_ALLOWED_HOSTS contains an invalid host: ${entry}`);
|
|
83
|
-
}
|
|
84
|
-
if (url.username || url.password || url.pathname !== '/' || url.search || url.hash || !url.host) {
|
|
85
|
-
throw new Error(`MCP_ALLOWED_HOSTS contains an invalid host: ${entry}`);
|
|
86
|
-
}
|
|
87
|
-
return url.host.toLowerCase();
|
|
88
|
-
}))];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
41
|
export function loadEnvFileIfPresent(path = '.env') {
|
|
92
42
|
try {
|
|
93
43
|
loadEnvFile(path);
|
|
@@ -131,15 +81,6 @@ export function loadConfig(env = process.env) {
|
|
|
131
81
|
},
|
|
132
82
|
},
|
|
133
83
|
logging: { level: readString(env.LOG_LEVEL, 'info') },
|
|
134
|
-
mcp: {
|
|
135
|
-
enabled: readBoolean(env.MCP_ENABLED, false),
|
|
136
|
-
writesEnabled: readBoolean(env.MCP_WRITES_ENABLED, false),
|
|
137
|
-
requireAuthentication: readBoolean(env.MCP_REQUIRE_AUTHENTICATION, true),
|
|
138
|
-
maxItems: readInteger(env.MCP_MAX_ITEMS, 100, 'MCP_MAX_ITEMS', { min: 1, max: 500 }),
|
|
139
|
-
maxResultBytes: readInteger(env.MCP_MAX_RESULT_BYTES, 1_000_000, 'MCP_MAX_RESULT_BYTES', { min: 10_000, max: 10_000_000 }),
|
|
140
|
-
allowedOrigins: readOriginList(env.MCP_ALLOWED_ORIGINS, studioOrigin),
|
|
141
|
-
allowedHosts: readHostList(env.MCP_ALLOWED_HOSTS, studioOrigin),
|
|
142
|
-
},
|
|
143
84
|
cache: {
|
|
144
85
|
enabled: readBoolean(env.CACHE_ENABLED, true),
|
|
145
86
|
store: cacheStore,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const mcpSettingsMigration = Object.freeze({
|
|
2
|
+
id: '0017-mcp-settings',
|
|
3
|
+
statements: [
|
|
4
|
+
`CREATE TABLE yuncms_mcp_settings (
|
|
5
|
+
id TINYINT UNSIGNED NOT NULL PRIMARY KEY,
|
|
6
|
+
enabled TINYINT(1) NOT NULL DEFAULT 0,
|
|
7
|
+
writes_enabled TINYINT(1) NOT NULL DEFAULT 0,
|
|
8
|
+
require_authentication TINYINT(1) NOT NULL DEFAULT 1,
|
|
9
|
+
allowed_origins JSON NOT NULL,
|
|
10
|
+
allowed_hosts JSON NOT NULL,
|
|
11
|
+
max_items INT UNSIGNED NOT NULL DEFAULT 100,
|
|
12
|
+
max_result_bytes INT UNSIGNED NOT NULL DEFAULT 1000000,
|
|
13
|
+
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
|
14
|
+
CONSTRAINT chk_yuncms_mcp_settings_singleton CHECK (id = 1)
|
|
15
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,
|
|
16
|
+
`INSERT INTO yuncms_mcp_settings (id, allowed_origins, allowed_hosts)
|
|
17
|
+
VALUES (1, JSON_ARRAY(), JSON_ARRAY())`,
|
|
18
|
+
],
|
|
19
|
+
});
|