@edgemodders/redcode-mcp 0.2.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,15 +1,30 @@
1
- # Changelog
2
-
3
- ## 0.2.0 (2026-10-16)
4
-
5
- The first version on npm, as `@edgemodders/redcode-mcp`. It needs REDbridge 0.8.0.
6
-
7
- - Run it with `npx -y @edgemodders/redcode-mcp`. Everything is bundled into one file, so an install pulls no other package. The bundled packages' notices are in `dist/ThirdPartyNotices.txt`.
8
- - TweakDB tools: `tweakdb_types`, `tweakdb_records`, `tweakdb_record`, `tweakdb_flat`, `tweakdb_search`, `tweakdb_names`.
9
- - Game sound tools: `sound_events`, `sound_extract`.
10
- - Project tools: `project_export`, `project_import`, `project_pack`.
11
- - Mod archive tools: `archive_open_mod`, `archive_extract_from_mod`.
12
-
13
- ## 0.1.0 (2026-10-12)
14
-
15
- Not published. The first tools were the archives, CR2W to JSON and back, textures, hashes, and document sessions with patches.
1
+ # Changelog
2
+
3
+ ## 0.4.0 (2026-10-27)
4
+
5
+ The new tool needs REDbridge 0.11.0, the bridge for game 2.5; the rest work as in 0.3.0.
6
+
7
+ - `mesh_items`: the TweakDB item records whose look shows a mesh, with the `.app` look, root entity and mesh appearance on the way. Needs REDbridge 0.11.0 or later.
8
+ - An empty `REDBRIDGE_EXE`, `REDCODE_GAME_PATH` or `DOTNET_ROOT` counts as unset, so REDcode's VS Code settings still apply. A Claude Code plugin passes an option left blank as an empty variable, which hid them before.
9
+
10
+ ## 0.3.0 (2026-10-16)
11
+
12
+ The new tools need REDbridge 0.10.0 (`loc_*`) and 0.9.0 (`sound_import`, `template`); the rest still work with 0.8.0.
13
+
14
+ - `loc_languages`, `loc_search` and `loc_get`: the game's on-screen texts (LocKeys), with a project's own over them. Each `loc_search` result names its onscreens file and row, for `cr2w_open` and `cr2w_get_node`. Needs REDbridge 0.10.0.
15
+ - `project_import` takes `template`, a game path or `.mesh` to start a new mesh from when a GLB has no mesh to import over, and a mesh import returns `mesh`: submeshes, the GLB's material names per submesh, material entries, appearances and what was resized. Needs REDbridge 0.9.0.
16
+ - `sound_import`: override game sound effects in a project from `<hash>.wav` files. Needs REDbridge 0.9.0.
17
+
18
+ ## 0.2.0 (2026-10-16)
19
+
20
+ The first version on npm, as `@edgemodders/redcode-mcp`. It needs REDbridge 0.8.0.
21
+
22
+ - Run it with `npx -y @edgemodders/redcode-mcp`. Everything is bundled into one file, so an install pulls no other package. The bundled packages' notices are in `dist/ThirdPartyNotices.txt`.
23
+ - TweakDB tools: `tweakdb_types`, `tweakdb_records`, `tweakdb_record`, `tweakdb_flat`, `tweakdb_search`, `tweakdb_names`.
24
+ - Game sound tools: `sound_events`, `sound_extract`.
25
+ - Project tools: `project_export`, `project_import`, `project_pack`.
26
+ - Mod archive tools: `archive_open_mod`, `archive_extract_from_mod`.
27
+
28
+ ## 0.1.0 (2026-10-12)
29
+
30
+ Not published. The first tools were the archives, CR2W to JSON and back, textures, hashes, and document sessions with patches.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Edgemodders
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Edgemodders
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,80 +1,83 @@
1
- # @edgemodders/redcode-mcp
2
-
3
- Cyberpunk 2077 modding tools for AI assistants, over the Model Context Protocol (MCP). An assistant such as Claude Code gets the same engine as REDcode, the Cyberpunk 2077 modding workspace for Visual Studio Code. It can:
4
- - search and extract from the game's archives
5
- - read CR2W files as WolvenKit JSON, write them back, and edit them node by node with patches
6
- - decode textures
7
- - read TweakDB
8
- - find and extract game sounds
9
- - export, import and pack a WolvenKit project
10
-
11
- The server starts [REDbridge](https://github.com/Edgemodders/REDbridge), which runs [WolvenKit](https://github.com/WolvenKit/WolvenKit)'s libraries, and talks to it over stdio. Nothing links WolvenKit into this package, so it stays MIT. Credit for the file formats and the engine belongs to the WolvenKit team.
12
-
13
- ## What you need
14
-
15
- - Windows, and Cyberpunk 2077 installed.
16
- - REDbridge. REDcode's **Install REDbridge** command fetches it, or you can unzip a [REDbridge release](https://github.com/Edgemodders/REDbridge/releases) anywhere.
17
- - The .NET 10 runtime: `winget install Microsoft.DotNet.Runtime.10`.
18
- - Node.js 20 or newer.
19
-
20
- ## Setup
21
-
22
- Register it with Claude Code:
23
-
24
- ```
25
- claude mcp add redcode -- npx -y @edgemodders/redcode-mcp
26
- ```
27
-
28
- Any other MCP client takes the same command: `npx -y @edgemodders/redcode-mcp`.
29
-
30
- The server needs REDbridge.exe and the game folder. It looks for them in order: the command line, then the environment, then the VS Code settings REDcode uses. So on a machine where REDcode is set up, it needs no configuration.
31
-
32
- | Setting | Command line | Environment | VS Code setting |
33
- |---|---|---|---|
34
- | REDbridge.exe, or the folder containing it | `--bridge <path>` | `REDBRIDGE_EXE` | `redcode.bridgePath` |
35
- | Game folder | `--game <path>` | `REDCODE_GAME_PATH` | `redcode.gamePath` |
36
- | Per-user .NET runtime | `--dotnet-root <path>` | `DOTNET_ROOT` | `redcode.dotnetRoot` |
37
-
38
- For example: `claude mcp add redcode -- npx -y @edgemodders/redcode-mcp --bridge "D:\Tools\REDbridge" --game "D:\Games\Cyberpunk 2077"`.
39
-
40
- The bridge starts on the first tool call and loads the archive index in about three seconds, then stays up for the session. If it dies, the next call restarts it, and any open document sessions are lost. The tools need REDbridge 0.8.0 or newer; TweakDB and the sound names need 0.8.0 and 0.7.2.
41
-
42
- To build from source, run `npm install` and `npm run mcp:build` in the [REDcode repository](https://github.com/Edgemodders/REDcode), then point the client at `packages/redcode-mcp/dist/server.cjs` with `node`.
43
-
44
- ## Tools
45
-
46
- | Tool | What it does |
47
- |---|---|
48
- | `bridge_info` | Versions, game folder, archive and file counts |
49
- | `archive_search`, `archive_list`, `archive_tree` | Find game files by substring, by prefix or archive with paging, or one folder level at a time |
50
- | `archive_extract`, `archive_extract_many` | Copy game files to disk as they are |
51
- | `cr2w_to_json`, `cr2w_file_to_json` | A game file or a file on disk as WolvenKit JSON, inline (cut at `maxChars`, 200,000 by default) or written to `outFile` |
52
- | `cr2w_from_json` | Write a CR2W file from WolvenKit JSON |
53
- | `texture_to_png`, `texture_file_to_png` | Decode an `.xbm` to PNG |
54
- | `hash_fnv1a64`, `hash_resolve` | Hash a string; look a hash up |
55
- | `archive_open_mod`, `archive_extract_from_mod` | List the resources in a mod's `.archive` on disk; extract them, by game path or hash. No game needed |
56
- | `project_export`, `project_import`, `project_pack` | WolvenKit's exporter (a mesh to glTF, a texture to an image, a `.wem` to Ogg) and importer between `source/archive` and `source/raw`; pack a folder into `<modName>.archive` |
57
- | `tweakdb_types`, `tweakdb_records`, `tweakdb_record`, `tweakdb_flat`, `tweakdb_search`, `tweakdb_names` | TweakDB as the game ships it, read offline from `tweakdb_ep1.bin` (the first call loads it, about 12 s). Records by type (subtypes included) or prefix with the flats you name, one record with every flat, one flat, a search by path words, and ids to paths. Record links come back as paths, LocKeys as `{ key, text }`. Bridge 0.8.0 |
58
- | `sound_events` | Find the game's sound effect events by words in their name or tags, each with the hashes of its sounds (its variations). Names from WolvenKit's event list, game 1.6. Bridge 0.7.2 |
59
- | `sound_extract` | Write an event's sounds, by name or hash, or single sounds by hash, as `.opus` files (Ogg Opus, 48 kHz) |
60
- | `cr2w_open`, `cr2w_get_node`, `cr2w_get_nodes`, `cr2w_search`, `cr2w_patch`, `cr2w_save`, `cr2w_close`, `cr2w_documents` | A document session: parse once, read one node or several at a time with arrays paged, search values and property names below a path, apply JSON Patch operations atomically with inverse operations returned, write through WolvenKit's writer |
61
- | `cr2w_describe_type`, `cr2w_create_instance` | A type's properties, defaults and enum values; a default instance to add |
62
- | `cr2w_read` | Open, read several paths, close, in one call |
63
- | `cr2w_edit_file` | Open, patch, save, close, in one call. With a game path and `outFile`, a modified copy; the archives are never changed |
64
-
65
- Paths are dotted from the root chunk and match WolvenKit's JSON: property names, array indices, `Data` under a handle or a buffer, `Chunks` under a package. The tool descriptions carry the three format rules an assistant has to know: numbers are 32-bit floats, `compiledData` is rebuilt from `components` on save, and defaults are not stored. The design is in [docs/design/cr2w-editor.md](../../docs/design/cr2w-editor.md).
66
-
67
- ## Example
68
-
69
- Make a copy of a game light with a bigger radius, in one call:
70
-
71
- ```json
72
- {
73
- "tool": "cr2w_edit_file",
74
- "path": "base\\entities\\lights\\photomode_arealight.ent",
75
- "outFile": "E:\\mods\\mymod\\source\\archive\\base\\entities\\lights\\photomode_arealight.ent",
76
- "ops": [{ "op": "replace", "path": "components.0.radius", "value": 12 }]
77
- }
78
- ```
79
-
80
- The result names the file written, its size, and the inverse operation that would undo the edit.
1
+ # @edgemodders/redcode-mcp
2
+
3
+ Cyberpunk 2077 modding tools for AI assistants, over the Model Context Protocol (MCP). An assistant such as Claude Code gets the same engine as REDcode, the Cyberpunk 2077 modding workspace for Visual Studio Code. It can:
4
+ - search and extract from the game's archives
5
+ - read CR2W files as WolvenKit JSON, write them back, and edit them node by node with patches
6
+ - decode textures
7
+ - read TweakDB
8
+ - find and extract game sounds
9
+ - export, import and pack a WolvenKit project
10
+
11
+ The server starts [REDbridge](https://github.com/Edgemodders/REDbridge), which runs [WolvenKit](https://github.com/WolvenKit/WolvenKit)'s libraries, and talks to it over stdio. Nothing links WolvenKit into this package, so it stays MIT. Credit for the file formats and the engine belongs to the WolvenKit team.
12
+
13
+ ## What you need
14
+
15
+ - Windows, and Cyberpunk 2077 installed.
16
+ - REDbridge. REDcode's **Install REDbridge** command fetches it, or you can unzip a [REDbridge release](https://github.com/Edgemodders/REDbridge/releases) anywhere.
17
+ - The .NET 10 runtime: `winget install Microsoft.DotNet.Runtime.10`.
18
+ - Node.js 20 or newer.
19
+
20
+ ## Setup
21
+
22
+ Register it with Claude Code:
23
+
24
+ ```
25
+ claude mcp add redcode -- npx -y @edgemodders/redcode-mcp
26
+ ```
27
+
28
+ Any other MCP client takes the same command: `npx -y @edgemodders/redcode-mcp`.
29
+
30
+ The server needs REDbridge.exe and the game folder. It looks for them in order: the command line, then the environment, then the VS Code settings REDcode uses. So on a machine where REDcode is set up, it needs no configuration.
31
+
32
+ | Setting | Command line | Environment | VS Code setting |
33
+ |---|---|---|---|
34
+ | REDbridge.exe, or the folder containing it | `--bridge <path>` | `REDBRIDGE_EXE` | `redcode.bridgePath` |
35
+ | Game folder | `--game <path>` | `REDCODE_GAME_PATH` | `redcode.gamePath` |
36
+ | Per-user .NET runtime | `--dotnet-root <path>` | `DOTNET_ROOT` | `redcode.dotnetRoot` |
37
+
38
+ For example: `claude mcp add redcode -- npx -y @edgemodders/redcode-mcp --bridge "D:\Tools\REDbridge" --game "D:\Games\Cyberpunk 2077"`.
39
+
40
+ The bridge starts on the first tool call and loads the archive index in about three seconds, then stays up for the session. If it dies, the next call restarts it, and any open document sessions are lost. Most tools work with REDbridge 0.8.0 or newer; `sound_import` and `project_import`'s `template` need 0.9.0, and the `loc_*` tools 0.10.0.
41
+
42
+ To build from source, run `npm install` and `npm run mcp:build` in the [REDcode repository](https://github.com/Edgemodders/REDcode), then point the client at `packages/redcode-mcp/dist/server.cjs` with `node`.
43
+
44
+ ## Tools
45
+
46
+ | Tool | What it does |
47
+ |---|---|
48
+ | `bridge_info` | Versions, game folder, archive and file counts |
49
+ | `archive_search`, `archive_list`, `archive_tree` | Find game files by substring, by prefix or archive with paging, or one folder level at a time |
50
+ | `archive_extract`, `archive_extract_many` | Copy game files to disk as they are |
51
+ | `cr2w_to_json`, `cr2w_file_to_json` | A game file or a file on disk as WolvenKit JSON, inline (cut at `maxChars`, 200,000 by default) or written to `outFile` |
52
+ | `cr2w_from_json` | Write a CR2W file from WolvenKit JSON |
53
+ | `texture_to_png`, `texture_file_to_png` | Decode an `.xbm` to PNG |
54
+ | `hash_fnv1a64`, `hash_resolve` | Hash a string; look a hash up |
55
+ | `archive_open_mod`, `archive_extract_from_mod` | List the resources in a mod's `.archive` on disk; extract them, by game path or hash. No game needed |
56
+ | `project_export`, `project_import`, `project_pack` | WolvenKit's exporter (a mesh to glTF, a texture to an image, a `.wem` to Ogg) and importer between `source/archive` and `source/raw`; pack a folder into `<modName>.archive` |
57
+ | `loc_languages`, `loc_search`, `loc_get` | The game's on-screen texts (LocKeys) per language, base game and Phantom Liberty, read offline. Search by LocKey, secondary key or words of the text; get by LocKey or secondary key. With `projectDir`, the project's own onscreens entries win. Bridge 0.10.0 |
58
+ | `tweakdb_types`, `tweakdb_records`, `tweakdb_record`, `tweakdb_flat`, `tweakdb_search`, `tweakdb_names` | TweakDB as the game ships it, read offline from `tweakdb_ep1.bin` (the first call loads it, about 12 s). Records by type (subtypes included) or prefix with the flats you name, one record with every flat, one flat, a search by path words, and ids to paths. Record links come back as paths, LocKeys as `{ key, text }`. Bridge 0.8.0 |
59
+ | `mesh_items` | The TweakDB item records whose look shows a mesh, each with its display name, the mesh appearance it shows, the `.app` look and the root entity on the way. Covers items that pick their look by `appearanceName`, such as clothing. Bridge 0.11.0 |
60
+ | `sound_events` | Find the game's sound effect events by words in their name or tags, each with the hashes of its sounds (its variations). Names from WolvenKit's event list, game 1.6. Bridge 0.7.2 |
61
+ | `sound_import` | Override game sound effects in a project from `<hash>.wav` files: the changed `.opuspak` files and a new index go into its `source/archive`. Bridge 0.9.0 |
62
+ | `sound_extract` | Write an event's sounds, by name or hash, or single sounds by hash, as `.opus` files (Ogg Opus, 48 kHz) |
63
+ | `cr2w_open`, `cr2w_get_node`, `cr2w_get_nodes`, `cr2w_search`, `cr2w_patch`, `cr2w_save`, `cr2w_close`, `cr2w_documents` | A document session: parse once, read one node or several at a time with arrays paged, search values and property names below a path, apply JSON Patch operations atomically with inverse operations returned, write through WolvenKit's writer |
64
+ | `cr2w_describe_type`, `cr2w_create_instance` | A type's properties, defaults and enum values; a default instance to add |
65
+ | `cr2w_read` | Open, read several paths, close, in one call |
66
+ | `cr2w_edit_file` | Open, patch, save, close, in one call. With a game path and `outFile`, a modified copy; the archives are never changed |
67
+
68
+ Paths are dotted from the root chunk and match WolvenKit's JSON: property names, array indices, `Data` under a handle or a buffer, `Chunks` under a package. The tool descriptions carry the three format rules an assistant has to know: numbers are 32-bit floats, `compiledData` is rebuilt from `components` on save, and defaults are not stored. The design is in [the CR2W editor design page](https://edgemodders.github.io/REDcode/design/cr2w-editor.html).
69
+
70
+ ## Example
71
+
72
+ Make a copy of a game light with a bigger radius, in one call:
73
+
74
+ ```json
75
+ {
76
+ "tool": "cr2w_edit_file",
77
+ "path": "base\\entities\\lights\\photomode_arealight.ent",
78
+ "outFile": "E:\\mods\\mymod\\source\\archive\\base\\entities\\lights\\photomode_arealight.ent",
79
+ "ops": [{ "op": "replace", "path": "components.0.radius", "value": 12 }]
80
+ }
81
+ ```
82
+
83
+ The result names the file written, its size, and the inverse operation that would undo the edit.
@@ -1,4 +1,4 @@
1
- @edgemodders/redcode-mcp 0.2.0 bundles the following packages into dist/server.cjs. Their notices follow.
1
+ @edgemodders/redcode-mcp 0.4.0 bundles the following packages into dist/server.cjs. Their notices follow.
2
2
 
3
3
  ------------------------------------------------------------------------------
4
4
 
package/dist/server.cjs CHANGED
@@ -2874,20 +2874,20 @@ var require_compile = __commonJS({
2874
2874
  var util_1 = require_util();
2875
2875
  var validate_1 = require_validate();
2876
2876
  var SchemaEnv = class {
2877
- constructor(env) {
2877
+ constructor(env2) {
2878
2878
  var _a;
2879
2879
  this.refs = {};
2880
2880
  this.dynamicAnchors = {};
2881
2881
  let schema;
2882
- if (typeof env.schema == "object")
2883
- schema = env.schema;
2884
- this.schema = env.schema;
2885
- this.schemaId = env.schemaId;
2886
- this.root = env.root || this;
2887
- this.baseId = (_a = env.baseId) !== null && _a !== void 0 ? _a : (0, resolve_1.normalizeId)(schema === null || schema === void 0 ? void 0 : schema[env.schemaId || "$id"]);
2888
- this.schemaPath = env.schemaPath;
2889
- this.localRefs = env.localRefs;
2890
- this.meta = env.meta;
2882
+ if (typeof env2.schema == "object")
2883
+ schema = env2.schema;
2884
+ this.schema = env2.schema;
2885
+ this.schemaId = env2.schemaId;
2886
+ this.root = env2.root || this;
2887
+ this.baseId = (_a = env2.baseId) !== null && _a !== void 0 ? _a : (0, resolve_1.normalizeId)(schema === null || schema === void 0 ? void 0 : schema[env2.schemaId || "$id"]);
2888
+ this.schemaPath = env2.schemaPath;
2889
+ this.localRefs = env2.localRefs;
2890
+ this.meta = env2.meta;
2891
2891
  this.$async = schema === null || schema === void 0 ? void 0 : schema.$async;
2892
2892
  this.refs = {};
2893
2893
  }
@@ -3071,15 +3071,15 @@ var require_compile = __commonJS({
3071
3071
  baseId = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schId);
3072
3072
  }
3073
3073
  }
3074
- let env;
3074
+ let env2;
3075
3075
  if (typeof schema != "boolean" && schema.$ref && !(0, util_1.schemaHasRulesButRef)(schema, this.RULES)) {
3076
3076
  const $ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schema.$ref);
3077
- env = resolveSchema.call(this, root, $ref);
3077
+ env2 = resolveSchema.call(this, root, $ref);
3078
3078
  }
3079
3079
  const { schemaId } = this.opts;
3080
- env = env || new SchemaEnv({ schema, schemaId, root, baseId });
3081
- if (env.schema !== env.root.schema)
3082
- return env;
3080
+ env2 = env2 || new SchemaEnv({ schema, schemaId, root, baseId });
3081
+ if (env2.schema !== env2.root.schema)
3082
+ return env2;
3083
3083
  return void 0;
3084
3084
  }
3085
3085
  }
@@ -4877,8 +4877,8 @@ var require_ref = __commonJS({
4877
4877
  schemaType: "string",
4878
4878
  code(cxt) {
4879
4879
  const { gen, schema: $ref, it } = cxt;
4880
- const { baseId, schemaEnv: env, validateName, opts, self } = it;
4881
- const { root } = env;
4880
+ const { baseId, schemaEnv: env2, validateName, opts, self } = it;
4881
+ const { root } = env2;
4882
4882
  if (($ref === "#" || $ref === "#/") && baseId === root.baseId)
4883
4883
  return callRootRef();
4884
4884
  const schOrEnv = compile_1.resolveRef.call(self, root, baseId, $ref);
@@ -4888,8 +4888,8 @@ var require_ref = __commonJS({
4888
4888
  return callValidate(schOrEnv);
4889
4889
  return inlineRefSchema(schOrEnv);
4890
4890
  function callRootRef() {
4891
- if (env === root)
4892
- return callRef(cxt, validateName, env, env.$async);
4891
+ if (env2 === root)
4892
+ return callRef(cxt, validateName, env2, env2.$async);
4893
4893
  const rootName = gen.scopeValue("root", { ref: root });
4894
4894
  return callRef(cxt, (0, codegen_1._)`${rootName}.validate`, root, root.$async);
4895
4895
  }
@@ -4919,14 +4919,14 @@ var require_ref = __commonJS({
4919
4919
  exports2.getValidate = getValidate;
4920
4920
  function callRef(cxt, v, sch, $async) {
4921
4921
  const { gen, it } = cxt;
4922
- const { allErrors, schemaEnv: env, opts } = it;
4922
+ const { allErrors, schemaEnv: env2, opts } = it;
4923
4923
  const passCxt = opts.passContext ? names_1.default.this : codegen_1.nil;
4924
4924
  if ($async)
4925
4925
  callAsyncRef();
4926
4926
  else
4927
4927
  callSyncRef();
4928
4928
  function callAsyncRef() {
4929
- if (!env.$async)
4929
+ if (!env2.$async)
4930
4930
  throw new Error("async schema referenced by sync schema");
4931
4931
  const valid = gen.let("valid");
4932
4932
  gen.try(() => {
@@ -25307,12 +25307,12 @@ var BridgeClient = class extends import_node_events.EventEmitter {
25307
25307
  return;
25308
25308
  }
25309
25309
  const args = this.options.gamePath ? ["--game", this.options.gamePath] : [];
25310
- const env = { ...process.env };
25310
+ const env2 = { ...process.env };
25311
25311
  if (this.options.dotnetRoot) {
25312
- env.DOTNET_ROOT = this.options.dotnetRoot;
25312
+ env2.DOTNET_ROOT = this.options.dotnetRoot;
25313
25313
  }
25314
25314
  this.stopping = false;
25315
- const child = (0, import_node_child_process.spawn)(this.options.exe, args, { env, stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
25315
+ const child = (0, import_node_child_process.spawn)(this.options.exe, args, { env: env2, stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
25316
25316
  this.process = child;
25317
25317
  readline.createInterface({ input: child.stderr }).on("line", (line) => this.emit("log", line));
25318
25318
  child.on("error", (error2) => {
@@ -25423,13 +25423,17 @@ var BridgeClient = class extends import_node_events.EventEmitter {
25423
25423
  return this.call("project.export", { file, archiveDir, rawDir, options: options ?? null });
25424
25424
  }
25425
25425
  /** Imports a raw file under rawDir back into archiveDir, through WolvenKit's importer. keep: an existing file keeps its settings. Bridge 0.7.0. */
25426
- importFile(file, rawDir, archiveDir, keep = true) {
25427
- return this.call("project.import", { file, rawDir, archiveDir, keep });
25426
+ importFile(file, rawDir, archiveDir, keep = true, template) {
25427
+ return this.call("project.import", { file, rawDir, archiveDir, keep, template: template ?? null });
25428
25428
  }
25429
25429
  /** The sound event names WolvenKit knows, with their Wwise hashes, which name the groups in an .opusinfo. Bridge 0.7.2. */
25430
25430
  soundEvents() {
25431
25431
  return this.call("sound.events", {});
25432
25432
  }
25433
+ /** Overrides game sound effects in a project from <hash>.wav files: the changed packs and index go under archiveDir. Bridge 0.9.0. */
25434
+ importWavs(wavFiles, archiveDir) {
25435
+ return this.call("sound.importWavs", { wavFiles, archiveDir });
25436
+ }
25433
25437
  // ---- tweakdb (bridge 0.8.0): read offline from the game's tweakdb.bin, loaded on the first call (about 12 s) ----
25434
25438
  tweakTypes() {
25435
25439
  return this.call("tweakdb.types", {});
@@ -25450,6 +25454,37 @@ var BridgeClient = class extends import_node_events.EventEmitter {
25450
25454
  tweakNames(ids) {
25451
25455
  return this.call("tweakdb.names", { ids });
25452
25456
  }
25457
+ /**
25458
+ * The item records whose look shows a mesh (a game path), and every .app look that shows it. With
25459
+ * appearance, only the looks that show that mesh appearance. Loads TweakDB on the first call. Bridge 0.11.0.
25460
+ */
25461
+ meshItems(path4, appearance, language = "en-us") {
25462
+ return this.call("mesh.items", { path: path4, appearance: appearance ?? null, language });
25463
+ }
25464
+ // ---- on-screen texts (bridge 0.10.0) ------------------------------------------------
25465
+ /** The languages with on-screen texts in the game, and whether the base game and Phantom Liberty have them. */
25466
+ locLanguages() {
25467
+ return this.call("loc.languages");
25468
+ }
25469
+ /**
25470
+ * On-screen texts: "LocKey#123" or a number finds that LocKey; otherwise every word must be in the
25471
+ * secondary key or a text. With projectDir, the project's onscreens entries win over the game's.
25472
+ */
25473
+ locSearch(query, language = "en-us", limit = 200, projectDir) {
25474
+ return this.call("loc.search", { query, language, limit, projectDir: projectDir ?? null });
25475
+ }
25476
+ /** Entries by LocKey number or secondary key, with or without "LocKey#"; null where there is no text. */
25477
+ locGet(keys, language = "en-us", projectDir) {
25478
+ return this.call("loc.get", { keys, language, projectDir: projectDir ?? null });
25479
+ }
25480
+ /** The project's onscreens files for a language: those its .xl files name, then any in source/archive under <language>\onscreens. */
25481
+ locProjectFiles(projectDir, language = "en-us") {
25482
+ return this.call("loc.projectFiles", { projectDir, language });
25483
+ }
25484
+ /** The project's onscreens file for a language, written empty (and named in an .xl) when it has none. */
25485
+ locNewFile(projectDir, modName, language = "en-us") {
25486
+ return this.call("loc.newFile", { projectDir, modName, language });
25487
+ }
25453
25488
  // ---- cr2w document sessions (bridge 0.5.0) -------------------------------------------
25454
25489
  /** Parses a file once and keeps it open in the bridge. A game path opens read-only. */
25455
25490
  openDocument(source) {
@@ -25498,10 +25533,13 @@ var BridgeClient = class extends import_node_events.EventEmitter {
25498
25533
  };
25499
25534
 
25500
25535
  // src/server.ts
25501
- var Version = "0.2.0";
25536
+ var Version = "0.4.0";
25502
25537
  function arg(name) {
25503
25538
  const i = process.argv.indexOf(name);
25504
- return i >= 0 ? process.argv[i + 1] : void 0;
25539
+ return i >= 0 ? process.argv[i + 1] || void 0 : void 0;
25540
+ }
25541
+ function env(name) {
25542
+ return process.env[name]?.trim() || void 0;
25505
25543
  }
25506
25544
  function vscodeSetting(key) {
25507
25545
  const base = process.platform === "win32" ? process.env.APPDATA : process.platform === "darwin" ? path3.join(process.env.HOME ?? "", "Library/Application Support") : path3.join(process.env.HOME ?? "", ".config");
@@ -25510,13 +25548,13 @@ function vscodeSetting(key) {
25510
25548
  try {
25511
25549
  const text = fs3.readFileSync(file, "utf8");
25512
25550
  const match = text.match(new RegExp(`"${key.replace(/\./g, "\\.")}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`));
25513
- return match ? JSON.parse(`"${match[1]}"`) : void 0;
25551
+ return match ? JSON.parse(`"${match[1]}"`) || void 0 : void 0;
25514
25552
  } catch {
25515
25553
  return void 0;
25516
25554
  }
25517
25555
  }
25518
25556
  function resolveBridgeExe() {
25519
- let exe = arg("--bridge") ?? process.env.REDBRIDGE_EXE ?? vscodeSetting("redcode.bridgePath") ?? "REDbridge.exe";
25557
+ let exe = arg("--bridge") ?? env("REDBRIDGE_EXE") ?? vscodeSetting("redcode.bridgePath") ?? "REDbridge.exe";
25520
25558
  if (exe && fs3.existsSync(exe) && fs3.statSync(exe).isDirectory()) {
25521
25559
  exe = path3.join(exe, "REDbridge.exe");
25522
25560
  }
@@ -25524,8 +25562,8 @@ function resolveBridgeExe() {
25524
25562
  }
25525
25563
  var config2 = {
25526
25564
  exe: resolveBridgeExe(),
25527
- gamePath: arg("--game") ?? process.env.REDCODE_GAME_PATH ?? vscodeSetting("redcode.gamePath"),
25528
- dotnetRoot: arg("--dotnet-root") ?? process.env.DOTNET_ROOT ?? vscodeSetting("redcode.dotnetRoot") ?? void 0
25565
+ gamePath: arg("--game") ?? env("REDCODE_GAME_PATH") ?? vscodeSetting("redcode.gamePath"),
25566
+ dotnetRoot: arg("--dotnet-root") ?? env("DOTNET_ROOT") ?? vscodeSetting("redcode.dotnetRoot") ?? void 0
25529
25567
  };
25530
25568
  var log = (line) => process.stderr.write(`[redcode-mcp] ${line}
25531
25569
  `);
@@ -25645,12 +25683,13 @@ tool("project_export", "Export a game file under a project's source/archive to t
25645
25683
  meshBinary: external_exports.boolean().optional().describe("Meshes as .glb (true, default) or .gltf."),
25646
25684
  meshMaterials: external_exports.boolean().optional().describe("Export a mesh's materials and textures too; default true.")
25647
25685
  }, (a, b) => b.exportFile(a.file, a.archiveDir, a.rawDir, { textureFormat: a.textureFormat, meshBinary: a.meshBinary, meshMaterials: a.meshMaterials }));
25648
- tool("project_import", "Import a raw file under a project's source/raw back into source/archive through WolvenKit's importer (images to .xbm, glTF or GLB to .mesh, WAV, CSV and so on). With keep (default), the file it replaces keeps its settings: a texture its group, format, compression and flags. Returns ok and the files written, relative to archiveDir. Bridge 0.7.0.", {
25686
+ tool("project_import", "Import a raw file under a project's source/raw back into source/archive through WolvenKit's importer (images to .xbm, glTF or GLB to .mesh, CSV and so on; sound effects go through sound_import). With keep (default), the file it replaces keeps its settings: a texture its group, format, compression and flags. A GLB imports only over an existing .mesh at the same path: for a new asset, pass template (a game path or .mesh to start from, bridge 0.9.0). A mesh import also returns mesh: submesh count, the GLB's material names per submesh, the mesh's material entries, appearances, and how appearances were sized to the new submeshes. Returns ok and the files written, relative to archiveDir. Bridge 0.7.0.", {
25649
25687
  file: external_exports.string().describe("The raw file, under rawDir."),
25650
25688
  rawDir: external_exports.string().describe("The project's source/raw folder."),
25651
25689
  archiveDir: external_exports.string().describe("The project's source/archive folder."),
25652
- keep: external_exports.boolean().optional().describe("Keep the replaced file's settings; default true.")
25653
- }, (a, b) => b.importFile(a.file, a.rawDir, a.archiveDir, a.keep ?? true));
25690
+ keep: external_exports.boolean().optional().describe("Keep the replaced file's settings; default true."),
25691
+ template: external_exports.string().optional().describe("For a GLB with no mesh to import over: a game path (base\\...mesh) or a .mesh file to start the new mesh from.")
25692
+ }, (a, b) => b.importFile(a.file, a.rawDir, a.archiveDir, a.keep ?? true, a.template));
25654
25693
  tool("project_pack", "Pack a folder (a project's source/archive) into <modName>.archive in outDir through WolvenKit's packer. Returns the archive path, size, file count and warnings for paths with capital letters or spaces. Bridge 0.6.1.", {
25655
25694
  dir: external_exports.string().describe("The folder to pack, laid out as game paths (base/..., ep1/...)."),
25656
25695
  outDir: external_exports.string().describe("Where the archive goes, usually packed/archive/pc/mod."),
@@ -25693,6 +25732,24 @@ tool("tweakdb_search", `TweakDB records whose path contains every word, any case
25693
25732
  tool("tweakdb_names", "Paths for TweakDB ids (0x hex or decimal) from WolvenKit's names list, null when unknown. Only the low 40 bits count, so ids with a flat-buffer offset match. Needs no TweakDB load. Bridge 0.8.0.", {
25694
25733
  ids: external_exports.array(external_exports.string())
25695
25734
  }, (a, b) => b.tweakNames(a.ids));
25735
+ tool("mesh_items", "The TweakDB item records whose look shows a mesh: the reverse of record > factory .csv > root .ent > .app look > mesh. Each item has record, type, displayName ({ key, text }), appearanceName, meshAppearance (the mesh appearance its look shows), app and look, and entity with entityAppearances (one per body and camera, such as s1_casual_01_basic_01_&Male). looks lists every .app look that shows the mesh, also those no item reaches (NPC appearances). Covers items that pick their look by appearanceName, such as clothing; not weapons, nor a mesh only a part entity holds. Loads TweakDB on the first call (about 10 s); the index after it takes under a second. Bridge 0.11.0.", {
25736
+ path: external_exports.string().describe("The mesh's game path, such as base\\characters\\garment\\player_equipment\\feet\\s1_055_shoe__converse\\s1_055_pma_shoe__converse.mesh."),
25737
+ appearance: external_exports.string().optional().describe("Only the looks that show this mesh appearance, such as red_yellow_graphic."),
25738
+ language: external_exports.string().optional().describe("Localization folder for display names, default en-us.")
25739
+ }, (a, b) => b.meshItems(a.path, a.appearance, a.language ?? "en-us"));
25740
+ var LocRules = "Languages are folder names such as en-us or de-de (default en-us). With projectDir, the project's onscreens entries replace the game's ones with the same key. The first call per language loads it, about 1 s. Bridge 0.10.0.";
25741
+ tool("loc_languages", "The languages the game has on-screen texts for, and whether Phantom Liberty has them too. Bridge 0.10.0.", {}, (_a, b) => b.locLanguages());
25742
+ tool("loc_search", `Find on-screen texts: 'LocKey#123' or a number finds that LocKey, otherwise every word must appear, any case, in the secondary key or the female or male text. Each result has key (the LocKey number as text), secondaryKey, female, male, source (base, ep1 or project), and file and index: its onscreens file (a game path, or relative to projectDir for a project entry) and its row there, for cr2w_open and cr2w_get_node. ${LocRules}`, {
25743
+ query: external_exports.string().describe("LocKey#123, a secondary key, or words such as 'militech pistol'."),
25744
+ language: external_exports.string().optional(),
25745
+ limit: external_exports.number().optional().describe("Most results to return (default 200)."),
25746
+ projectDir: external_exports.string().optional().describe("A REDcode or WolvenKit project folder whose own texts should count.")
25747
+ }, (a, b) => b.locSearch(a.query, a.language ?? "en-us", a.limit ?? 200, a.projectDir));
25748
+ tool("loc_get", `The texts for LocKeys, by number or secondary key, with or without 'LocKey#'; null where there is none. ${LocRules}`, {
25749
+ keys: external_exports.array(external_exports.string()).describe("Such as ['LocKey#12345', 'Gameplay-Devices-Computers-Common-NewsFeed']."),
25750
+ language: external_exports.string().optional(),
25751
+ projectDir: external_exports.string().optional()
25752
+ }, (a, b) => b.locGet(a.keys, a.language ?? "en-us", a.projectDir));
25696
25753
  var soundCache = path3.join(os.tmpdir(), "redcode-mcp", "sounds");
25697
25754
  var soundIndex;
25698
25755
  async function gameSounds(b) {
@@ -25713,6 +25770,10 @@ tool("sound_events", "Find the game's sound effect events by name or tag (words
25713
25770
  const hits = events.events.filter((e) => groups.has(e.hash) && words.every((w) => `${e.name} ${e.tags.join(" ")}`.toLowerCase().includes(w)));
25714
25771
  return { total: hits.length, events: hits.slice(0, a.limit ?? 100).map((e) => ({ ...e, sounds: groups.get(e.hash).map((i) => index.hashes[i]) })) };
25715
25772
  });
25773
+ tool("sound_import", "Override game sound effects in a WolvenKit project from WAV files, each named after the sound's hash (832549876.wav; sound_events gives the hashes). Writes the changed .opuspak files and a new sfx_container.opusinfo under the project's source/archive; earlier overrides there stay. Any rate and 16-bit, 24-bit or float WAV; encoded at 48 kHz Opus. Bridge 0.9.0.", {
25774
+ wavFiles: external_exports.array(external_exports.string()).describe("Absolute paths of <hash>.wav files."),
25775
+ archiveDir: external_exports.string().describe("The project's source/archive folder.")
25776
+ }, (a, b) => b.importWavs(a.wavFiles, a.archiveDir));
25716
25777
  tool("sound_extract", "Write game sound effects as .opus files (Ogg Opus, 48 kHz) named by sound hash: every variation of an event, by name or hash, or single sounds by hash. Voice lines are .wem files instead: use project_export.", {
25717
25778
  event: external_exports.string().optional().describe("An event name, such as w_gun_npc_nova_reload, or its decimal hash."),
25718
25779
  sounds: external_exports.array(external_exports.number()).optional().describe("Or sound hashes."),
package/package.json CHANGED
@@ -1,57 +1,57 @@
1
- {
2
- "name": "@edgemodders/redcode-mcp",
3
- "version": "0.2.0",
4
- "description": "Cyberpunk 2077 modding tools for AI assistants over MCP: the game archives, CR2W files as JSON and node-by-node edits, textures, TweakDB, game sounds, export, import and pack, through REDbridge and WolvenKit's engine.",
5
- "license": "MIT",
6
- "author": "Edgemodders",
7
- "homepage": "https://edgemodders.github.io/REDcode/mcp.html",
8
- "bugs": {
9
- "url": "https://github.com/Edgemodders/REDcode/issues"
10
- },
11
- "repository": {
12
- "type": "git",
13
- "url": "https://github.com/Edgemodders/REDcode",
14
- "directory": "packages/redcode-mcp"
15
- },
16
- "keywords": [
17
- "cyberpunk",
18
- "cyberpunk2077",
19
- "modding",
20
- "wolvenkit",
21
- "mcp",
22
- "mcp-server",
23
- "model-context-protocol",
24
- "redcode",
25
- "tweakdb"
26
- ],
27
- "bin": {
28
- "redcode-mcp": "dist/server.cjs"
29
- },
30
- "files": [
31
- "dist/server.cjs",
32
- "dist/ThirdPartyNotices.txt",
33
- "README.md",
34
- "CHANGELOG.md",
35
- "LICENSE"
36
- ],
37
- "scripts": {
38
- "build": "node esbuild.mjs",
39
- "typecheck": "tsc -p tsconfig.json --noEmit",
40
- "start": "node dist/server.cjs"
41
- },
42
- "devDependencies": {
43
- "@modelcontextprotocol/sdk": "^1.32.1",
44
- "vscode-jsonrpc": "^9.0.3",
45
- "zod": "^3.25.0",
46
- "@types/node": "^24.0.0",
47
- "esbuild": "^0.28.2",
48
- "typescript": "~5.9.3"
49
- },
50
- "engines": {
51
- "node": ">=20"
52
- },
53
- "publishConfig": {
54
- "access": "public",
55
- "provenance": true
56
- }
57
- }
1
+ {
2
+ "name": "@edgemodders/redcode-mcp",
3
+ "version": "0.4.0",
4
+ "description": "Cyberpunk 2077 modding tools for AI assistants over MCP: the game archives, CR2W files as JSON and node-by-node edits, textures, TweakDB, game sounds, export, import and pack, through REDbridge and WolvenKit's engine.",
5
+ "license": "MIT",
6
+ "author": "Edgemodders",
7
+ "homepage": "https://edgemodders.github.io/REDcode/mcp.html",
8
+ "bugs": {
9
+ "url": "https://github.com/Edgemodders/REDcode/issues"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/Edgemodders/REDcode.git",
14
+ "directory": "packages/redcode-mcp"
15
+ },
16
+ "keywords": [
17
+ "cyberpunk",
18
+ "cyberpunk2077",
19
+ "modding",
20
+ "wolvenkit",
21
+ "mcp",
22
+ "mcp-server",
23
+ "model-context-protocol",
24
+ "redcode",
25
+ "tweakdb"
26
+ ],
27
+ "bin": {
28
+ "redcode-mcp": "dist/server.cjs"
29
+ },
30
+ "files": [
31
+ "dist/server.cjs",
32
+ "dist/ThirdPartyNotices.txt",
33
+ "README.md",
34
+ "CHANGELOG.md",
35
+ "LICENSE"
36
+ ],
37
+ "scripts": {
38
+ "build": "node esbuild.mjs",
39
+ "typecheck": "tsc -p tsconfig.json --noEmit",
40
+ "start": "node dist/server.cjs"
41
+ },
42
+ "devDependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.32.1",
44
+ "vscode-jsonrpc": "^9.0.3",
45
+ "zod": "^3.25.0",
46
+ "@types/node": "^24.0.0",
47
+ "esbuild": "^0.28.2",
48
+ "typescript": "~5.9.3"
49
+ },
50
+ "engines": {
51
+ "node": ">=20"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public",
55
+ "provenance": true
56
+ }
57
+ }