spacecode 2.5.6 → 2.5.7

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/dist/repl.mjs CHANGED
@@ -4988,6 +4988,76 @@ var init_skill_file = __esm({
4988
4988
  }
4989
4989
  });
4990
4990
 
4991
+ // ../agent/src/import/claude-json-mcp.ts
4992
+ function normalizeClaudeJsonMcp(text, projectPaths) {
4993
+ const errors = [];
4994
+ let parsed;
4995
+ try {
4996
+ parsed = JSON.parse(text);
4997
+ } catch (err) {
4998
+ if (!(err instanceof SyntaxError)) throw err;
4999
+ return { servers: [], errors: ["not valid JSON"] };
5000
+ }
5001
+ const root = asObject(parsed) ?? {};
5002
+ const serverMap = /* @__PURE__ */ new Map();
5003
+ const processEntry = (name, raw, scope) => {
5004
+ const entry = asObject(raw);
5005
+ if (!entry) {
5006
+ errors.push(`${name}: not an object`);
5007
+ return;
5008
+ }
5009
+ const hasUrl = typeof entry.url === "string";
5010
+ const hasCommand = typeof entry.command === "string";
5011
+ let transport;
5012
+ let url = "";
5013
+ let command = "";
5014
+ let args = [];
5015
+ if (hasUrl && !hasCommand) {
5016
+ transport = "http";
5017
+ url = entry.url;
5018
+ } else if (hasCommand) {
5019
+ transport = "stdio";
5020
+ command = entry.command;
5021
+ if (Array.isArray(entry.args)) args = entry.args.filter((a) => typeof a === "string");
5022
+ } else {
5023
+ errors.push(`${name}: needs a command or a url`);
5024
+ return;
5025
+ }
5026
+ const rawEnv = transport === "http" ? asObject(entry.headers) : asObject(entry.env);
5027
+ const env3 = {};
5028
+ const literalKeys = /* @__PURE__ */ new Set();
5029
+ if (rawEnv) {
5030
+ for (const [k2, v2] of Object.entries(rawEnv)) {
5031
+ if (typeof v2 !== "string") continue;
5032
+ if (v2.includes("${")) {
5033
+ env3[k2] = v2;
5034
+ } else {
5035
+ env3[k2] = "${" + literalVarName(name, k2) + "}";
5036
+ literalKeys.add(k2);
5037
+ }
5038
+ }
5039
+ }
5040
+ serverMap.set(name, { name, scope, transport, command, args, url, env: env3, literalKeys: [...literalKeys].sort() });
5041
+ };
5042
+ for (const [name, raw] of Object.entries(asObject(root.mcpServers) ?? {})) processEntry(name, raw, "user");
5043
+ const projects = asObject(root.projects) ?? {};
5044
+ for (const p of projectPaths) {
5045
+ const servers = asObject(asObject(projects[p])?.mcpServers);
5046
+ if (!servers) continue;
5047
+ for (const [name, raw] of Object.entries(servers)) processEntry(name, raw, "project");
5048
+ }
5049
+ return { servers: [...serverMap.values()].sort((a, b2) => a.name < b2.name ? -1 : a.name > b2.name ? 1 : 0), errors };
5050
+ }
5051
+ var sanitize, literalVarName, asObject;
5052
+ var init_claude_json_mcp = __esm({
5053
+ "../agent/src/import/claude-json-mcp.ts"() {
5054
+ "use strict";
5055
+ sanitize = (s) => s.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
5056
+ literalVarName = (server, key) => `MCP_${sanitize(server)}_${sanitize(key)}`;
5057
+ asObject = (v2) => v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
5058
+ }
5059
+ });
5060
+
4991
5061
  // ../agent/src/import/tool-names.ts
4992
5062
  function translateToolNames(text) {
4993
5063
  const counts = /* @__PURE__ */ new Map();
@@ -5169,6 +5239,7 @@ async function readClaudeAssets({ fs: fs6, home, projectDirs }) {
5169
5239
  }
5170
5240
  await readCommands(fs6, home, report);
5171
5241
  await readSkills(fs6, home, report);
5242
+ await readMcpServers(fs6, home, projectDirs, report);
5172
5243
  for (const dir of projectDirs) {
5173
5244
  const memDir = `${home}/.claude/projects/${claudeProjectSlug(dir)}/memory`;
5174
5245
  let entries;
@@ -5311,12 +5382,51 @@ async function readSkills(fs6, home, report) {
5311
5382
  report.assets.push({ kind: "skill", name: parsed.name, description: parsed.description, content: text, source: skillPath }, ...siblings);
5312
5383
  }
5313
5384
  }
5385
+ async function readMcpServers(fs6, home, projectDirs, report) {
5386
+ const path12 = `${home}/.claude.json`;
5387
+ const text = await readOptional(fs6, path12, report);
5388
+ if (text === null) return;
5389
+ const { servers, errors } = normalizeClaudeJsonMcp(text, projectDirs);
5390
+ for (const e of errors) report.errors.push({ path: path12, message: e });
5391
+ const seen = /* @__PURE__ */ new Set();
5392
+ for (const s of servers) {
5393
+ if (!NAME_RE3.test(s.name)) {
5394
+ report.errors.push({ path: path12, message: `MCP server name "${s.name}" must use only letters, digits, . _ - (max 100)` });
5395
+ continue;
5396
+ }
5397
+ if (seen.has(s.name.toLowerCase())) {
5398
+ report.errors.push({ path: path12, message: `MCP server name collision on "${s.name}" (names are case-insensitive)` });
5399
+ continue;
5400
+ }
5401
+ seen.add(s.name.toLowerCase());
5402
+ const { literalKeys, ...def } = s;
5403
+ const content = JSON.stringify(def, null, 2);
5404
+ if (Buffer.byteLength(content, "utf8") > MAX_ASSET_BYTES) {
5405
+ report.errors.push({ path: path12, message: `MCP server "${s.name}" skipped: over the ${MAX_ASSET_BYTES} byte asset limit` });
5406
+ continue;
5407
+ }
5408
+ if (literalKeys.length > 0) {
5409
+ report.notes.push(`MCP server "${s.name}": literal values for ${literalKeys.join(", ")} were NOT imported; set ${literalKeys.map((k2) => literalVarName(s.name, k2)).join(", ")} in your environment`);
5410
+ }
5411
+ if (s.transport === "http" && /[?@]/.test(s.url.replace(/^https?:\/\//, ""))) {
5412
+ report.notes.push(`MCP server "${s.name}": its url contains a query string or credentials, which are stored as written; move any token into a header or environment variable`);
5413
+ }
5414
+ report.assets.push({
5415
+ kind: "mcp",
5416
+ name: s.name,
5417
+ description: s.transport === "stdio" ? `stdio: ${s.command}` : `http: ${s.url}`,
5418
+ content,
5419
+ source: `${path12} (${s.scope})`
5420
+ });
5421
+ }
5422
+ }
5314
5423
  var MAX_ASSET_BYTES, NAME_RE3, COMMAND_NAME_RE, SKILL_NAME_RE, MAX_SKILL_FILES;
5315
5424
  var init_claude_sources = __esm({
5316
5425
  "../agent/src/import/claude-sources.ts"() {
5317
5426
  "use strict";
5318
5427
  init_command_file();
5319
5428
  init_skill_file();
5429
+ init_claude_json_mcp();
5320
5430
  init_skill_source();
5321
5431
  MAX_ASSET_BYTES = 256 * 1024;
5322
5432
  NAME_RE3 = /^[A-Za-z0-9._-]{1,100}$/;
@@ -6135,6 +6245,13 @@ var init_mcp_json_summary = __esm({
6135
6245
  }
6136
6246
  });
6137
6247
 
6248
+ // ../agent/src/import/mcp-env-expand.ts
6249
+ var init_mcp_env_expand = __esm({
6250
+ "../agent/src/import/mcp-env-expand.ts"() {
6251
+ "use strict";
6252
+ }
6253
+ });
6254
+
6138
6255
  // ../agent/src/index.ts
6139
6256
  var init_src = __esm({
6140
6257
  "../agent/src/index.ts"() {
@@ -6184,6 +6301,8 @@ var init_src = __esm({
6184
6301
  init_skill_tool();
6185
6302
  init_skill_source();
6186
6303
  init_mcp_json_summary();
6304
+ init_mcp_env_expand();
6305
+ init_claude_json_mcp();
6187
6306
  }
6188
6307
  });
6189
6308
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacecode",
3
- "version": "2.5.6",
3
+ "version": "2.5.7",
4
4
  "description": "SpaceCode - launch cloud dev environments with AI coding agents",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",