@corva/ui 3.77.0-12 → 3.77.0-14
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/MCP_README.md +9 -6
- package/README.md +6 -0
- package/mcp-server/server.mjs +1 -1
- package/mcp-server/setup.mjs +129 -44
- package/package.json +3 -2
package/MCP_README.md
CHANGED
|
@@ -215,12 +215,15 @@ This creates `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor), and `.codex
|
|
|
215
215
|
Restart your IDE or reload MCP servers afterward.
|
|
216
216
|
|
|
217
217
|
> If any of these files already exist, the setup command **extends** them — your existing MCP server entries are
|
|
218
|
-
> preserved, and
|
|
219
|
-
>
|
|
220
|
-
> `CORVA_UI_MCP_TELEMETRY_DISABLED`,
|
|
221
|
-
>
|
|
222
|
-
>
|
|
223
|
-
>
|
|
218
|
+
> preserved, and the `corva-ui` entry (`corva_ui` in the Codex TOML) is added only when it is missing. An entry that
|
|
219
|
+
> already exists is kept exactly as it is, so anything you added by hand, such as an `env` block with
|
|
220
|
+
> `CORVA_UI_MCP_TELEMETRY_DISABLED`, a `startup_timeout_sec`, or the Windows `cmd /c` form, survives a re-run after an
|
|
221
|
+
> upgrade; the command prints `(kept)` for that file. Pass `--force` to replace the entry with the stock one. A write
|
|
222
|
+
> re-serializes the whole file, so when the command adds or replaces an entry in the Codex TOML, comments and hand
|
|
223
|
+
> formatting are lost (the command says so in its output). A file that fails to parse is never written: the command
|
|
224
|
+
> prints the parse error and the path, and exits with a non-zero code. On native Windows the generated entry uses plain
|
|
225
|
+
> `npx`; if your client cannot spawn it, apply the `cmd /c` form from the Native Windows note under
|
|
226
|
+
> [Local Setup](#local-setup-recommended).
|
|
224
227
|
|
|
225
228
|
> **Monorepo:** Run the setup command from the monorepo root, not from individual
|
|
226
229
|
> `apps/*` directories. See [Monorepo Setup](#monorepo-setup) for details.
|
package/README.md
CHANGED
|
@@ -9,6 +9,12 @@ develop: [;
|
|
1053
|
+
const errorMessage = error => error instanceof Error ? error.message : String(error);
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Adds the stock `corva-ui` entry to one config file.
|
|
1057
|
+
*
|
|
1058
|
+
* An entry that already exists is left as it is, whatever it contains: developers add `env`,
|
|
1059
|
+
* timeouts and the Windows `cmd /c` form by hand, and a re-run after an upgrade must not drop
|
|
1060
|
+
* them. Only `force` replaces it. A file that does not parse is never written.
|
|
1061
|
+
*/
|
|
1062
1062
|
const writeConfig = ({
|
|
1063
|
-
|
|
1064
|
-
|
|
1063
|
+
target,
|
|
1064
|
+
force = false,
|
|
1065
|
+
cwd = process.cwd()
|
|
1065
1066
|
}) => {
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
}
|
|
1067
|
+
const {
|
|
1068
|
+
path: filePath,
|
|
1069
|
+
format
|
|
1070
|
+
} = target;
|
|
1071
|
+
const absolutePath = resolve(cwd, filePath);
|
|
1072
|
+
const fileExists = existsSync(absolutePath);
|
|
1070
1073
|
let existing = {};
|
|
1071
|
-
|
|
1072
|
-
if (existsSync(absolutePath)) {
|
|
1073
|
-
action = 'updated';
|
|
1074
|
+
if (fileExists) {
|
|
1074
1075
|
const raw = readFileSync(absolutePath, 'utf-8');
|
|
1076
|
+
let parsed;
|
|
1075
1077
|
try {
|
|
1076
|
-
|
|
1077
|
-
} catch {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1078
|
+
parsed = format.parse(raw);
|
|
1079
|
+
} catch (error) {
|
|
1080
|
+
return {
|
|
1081
|
+
file: filePath,
|
|
1082
|
+
action: 'failed',
|
|
1083
|
+
note: `invalid ${format.formatName}, file left unchanged: ${errorMessage(error)}`
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
if (!isRecord(parsed)) {
|
|
1087
|
+
return {
|
|
1088
|
+
file: filePath,
|
|
1089
|
+
action: 'failed',
|
|
1090
|
+
note: `invalid ${format.formatName}, file left unchanged: top level is not an object`
|
|
1091
|
+
};
|
|
1081
1092
|
}
|
|
1093
|
+
existing = parsed;
|
|
1082
1094
|
}
|
|
1083
|
-
const
|
|
1084
|
-
|
|
1095
|
+
const serversValue = existing[format.serversKey];
|
|
1096
|
+
|
|
1097
|
+
// A servers block that is not a plain object (an array, a string, null) cannot hold the entry.
|
|
1098
|
+
// Writing over it would drop whatever it held, so refuse instead of guessing.
|
|
1099
|
+
if (serversValue !== undefined && !isRecord(serversValue)) {
|
|
1100
|
+
return {
|
|
1101
|
+
file: filePath,
|
|
1102
|
+
action: 'failed',
|
|
1103
|
+
note: `"${format.serversKey}" is not an object, file left unchanged`
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
const servers = serversValue ?? {};
|
|
1107
|
+
const hasEntry = Object.hasOwn(servers, format.serverName);
|
|
1108
|
+
if (hasEntry && !force) {
|
|
1085
1109
|
return {
|
|
1086
1110
|
file: filePath,
|
|
1087
|
-
action: '
|
|
1111
|
+
action: 'kept',
|
|
1112
|
+
note: `entry "${format.serverName}" exists; pass --force to replace it with the stock entry`
|
|
1088
1113
|
};
|
|
1089
1114
|
}
|
|
1090
1115
|
const merged = {
|
|
@@ -1094,30 +1119,90 @@ const writeConfig = ({
|
|
|
1094
1119
|
[format.serverName]: SERVER_CONFIG
|
|
1095
1120
|
}
|
|
1096
1121
|
};
|
|
1122
|
+
mkdirSync(dirname(absolutePath), {
|
|
1123
|
+
recursive: true
|
|
1124
|
+
});
|
|
1097
1125
|
writeFileSync(absolutePath, `${format.serialize(merged)}\n`);
|
|
1126
|
+
let action = 'created';
|
|
1127
|
+
if (hasEntry) {
|
|
1128
|
+
action = 'replaced';
|
|
1129
|
+
} else if (fileExists) {
|
|
1130
|
+
action = 'added';
|
|
1131
|
+
}
|
|
1132
|
+
const note = fileExists && format.formatName === 'TOML' ? 'the file was rewritten; TOML comments and hand formatting are removed' : undefined;
|
|
1098
1133
|
return {
|
|
1099
1134
|
file: filePath,
|
|
1100
|
-
action
|
|
1135
|
+
action,
|
|
1136
|
+
note
|
|
1101
1137
|
};
|
|
1102
1138
|
};
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1139
|
+
|
|
1140
|
+
const ACTION_SYMBOLS = {
|
|
1141
|
+
created: '+',
|
|
1142
|
+
added: '+',
|
|
1143
|
+
replaced: '~',
|
|
1144
|
+
kept: '=',
|
|
1145
|
+
failed: '!'
|
|
1146
|
+
};
|
|
1147
|
+
const formatResult = ({
|
|
1148
|
+
file,
|
|
1149
|
+
action,
|
|
1150
|
+
note
|
|
1151
|
+
}) => ` ${ACTION_SYMBOLS[action]} ${file} (${action})${note ? `: ${note}` : ''}`;
|
|
1152
|
+
|
|
1153
|
+
/** Runs the setup over every target and collects the report. Never throws for a single target. */
|
|
1154
|
+
const runSetup = ({
|
|
1155
|
+
force = false,
|
|
1156
|
+
cwd = process.cwd()
|
|
1157
|
+
} = {}) => {
|
|
1158
|
+
const results = TARGETS.map(target => {
|
|
1107
1159
|
try {
|
|
1108
|
-
|
|
1160
|
+
return writeConfig({
|
|
1161
|
+
target,
|
|
1162
|
+
force,
|
|
1163
|
+
cwd
|
|
1164
|
+
});
|
|
1109
1165
|
} catch (error) {
|
|
1110
1166
|
const message = error instanceof Error ? error.message : String(error);
|
|
1111
|
-
|
|
1167
|
+
return {
|
|
1168
|
+
file: target.path,
|
|
1169
|
+
action: 'failed',
|
|
1170
|
+
note: `write failed: ${message}`
|
|
1171
|
+
};
|
|
1112
1172
|
}
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
for (const {
|
|
1116
|
-
file,
|
|
1173
|
+
});
|
|
1174
|
+
const failed = results.filter(({
|
|
1117
1175
|
action
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1176
|
+
}) => action === 'failed');
|
|
1177
|
+
const lines = ['Setting up @corva/ui MCP server configs...', '', ...results.map(formatResult), '', failed.length > 0 ? `${failed.length} of ${results.length} config files were not written. Fix the errors above and run the command again.` : 'Done! The corva-ui MCP server is now configured for this project.'];
|
|
1178
|
+
return {
|
|
1179
|
+
lines,
|
|
1180
|
+
exitCode: failed.length > 0 ? 1 : 0,
|
|
1181
|
+
results
|
|
1182
|
+
};
|
|
1183
|
+
};
|
|
1184
|
+
|
|
1185
|
+
/**
|
|
1186
|
+
* corva-ui-mcp-setup CLI
|
|
1187
|
+
*
|
|
1188
|
+
* Adds the @corva/ui MCP server entry to the MCP config files of the current project, so AI
|
|
1189
|
+
* coding agents can discover it. Run from any project root: npx -p @corva/ui corva-ui-mcp-setup
|
|
1190
|
+
*
|
|
1191
|
+
* An entry that already exists is kept as it is; `--force` replaces it with the stock entry.
|
|
1192
|
+
* The logic lives in src/setup so the Jest suite can cover it; this file only parses argv,
|
|
1193
|
+
* prints the report and sets the exit code.
|
|
1194
|
+
*/
|
|
1195
|
+
const main = () => {
|
|
1196
|
+
const force = process.argv.includes('--force');
|
|
1197
|
+
const {
|
|
1198
|
+
lines,
|
|
1199
|
+
exitCode
|
|
1200
|
+
} = runSetup({
|
|
1201
|
+
force
|
|
1202
|
+
});
|
|
1203
|
+
for (const line of lines) {
|
|
1204
|
+
console.log(line);
|
|
1120
1205
|
}
|
|
1121
|
-
|
|
1206
|
+
process.exitCode = exitCode;
|
|
1122
1207
|
};
|
|
1123
1208
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corva/ui",
|
|
3
|
-
"version": "3.77.0-
|
|
3
|
+
"version": "3.77.0-14",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"description": "Shared components/utils for Corva ui projects",
|
|
6
6
|
"repository": {
|
|
@@ -203,7 +203,6 @@
|
|
|
203
203
|
"react-number-format": "^4.7.3",
|
|
204
204
|
"react-player": "1.9.3",
|
|
205
205
|
"react-refresh": "^0.14.0",
|
|
206
|
-
"react-router-dom": "^5.3.4",
|
|
207
206
|
"react-s3-uploader": "4.8.0",
|
|
208
207
|
"react-sizeme": "2.5.2",
|
|
209
208
|
"react-sortable-hoc": "0.6.8",
|
|
@@ -228,6 +227,7 @@
|
|
|
228
227
|
"devDependencies": {
|
|
229
228
|
"react": "^18.3.1",
|
|
230
229
|
"react-dom": "^18.3.1",
|
|
230
|
+
"react-router-dom": "^5.3.4",
|
|
231
231
|
"@types/react": "18.2.42",
|
|
232
232
|
"@types/react-dom": "^18.2",
|
|
233
233
|
"@babel/core": "^7.12.10",
|
|
@@ -313,6 +313,7 @@
|
|
|
313
313
|
"peerDependencies": {
|
|
314
314
|
"react": "^18.3.1",
|
|
315
315
|
"react-dom": "^18.3.1",
|
|
316
|
+
"react-router-dom": "^5.3.4",
|
|
316
317
|
"@types/react": "18.2.42",
|
|
317
318
|
"@types/react-dom": "^18.2"
|
|
318
319
|
},
|