@edgemodders/redcode-mcp 0.2.0 → 0.3.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 +8 -0
- package/README.md +3 -1
- package/dist/ThirdPartyNotices.txt +1 -1
- package/dist/server.cjs +52 -6
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 (2026-10-16)
|
|
4
|
+
|
|
5
|
+
The new tools need REDbridge 0.10.0 (`loc_*`) and 0.9.0 (`sound_import`, `template`); the rest still work with 0.8.0.
|
|
6
|
+
|
|
7
|
+
- `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.
|
|
8
|
+
- `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.
|
|
9
|
+
- `sound_import`: override game sound effects in a project from `<hash>.wav` files. Needs REDbridge 0.9.0.
|
|
10
|
+
|
|
3
11
|
## 0.2.0 (2026-10-16)
|
|
4
12
|
|
|
5
13
|
The first version on npm, as `@edgemodders/redcode-mcp`. It needs REDbridge 0.8.0.
|
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ The server needs REDbridge.exe and the game folder. It looks for them in order:
|
|
|
37
37
|
|
|
38
38
|
For example: `claude mcp add redcode -- npx -y @edgemodders/redcode-mcp --bridge "D:\Tools\REDbridge" --game "D:\Games\Cyberpunk 2077"`.
|
|
39
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.
|
|
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
41
|
|
|
42
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
43
|
|
|
@@ -54,8 +54,10 @@ To build from source, run `npm install` and `npm run mcp:build` in the [REDcode
|
|
|
54
54
|
| `hash_fnv1a64`, `hash_resolve` | Hash a string; look a hash up |
|
|
55
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
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 |
|
|
57
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 |
|
|
58
59
|
| `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 |
|
|
60
|
+
| `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 |
|
|
59
61
|
| `sound_extract` | Write an event's sounds, by name or hash, or single sounds by hash, as `.opus` files (Ogg Opus, 48 kHz) |
|
|
60
62
|
| `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
63
|
| `cr2w_describe_type`, `cr2w_create_instance` | A type's properties, defaults and enum values; a default instance to add |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
@edgemodders/redcode-mcp 0.
|
|
1
|
+
@edgemodders/redcode-mcp 0.3.0 bundles the following packages into dist/server.cjs. Their notices follow.
|
|
2
2
|
|
|
3
3
|
------------------------------------------------------------------------------
|
|
4
4
|
|
package/dist/server.cjs
CHANGED
|
@@ -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,30 @@ var BridgeClient = class extends import_node_events.EventEmitter {
|
|
|
25450
25454
|
tweakNames(ids) {
|
|
25451
25455
|
return this.call("tweakdb.names", { ids });
|
|
25452
25456
|
}
|
|
25457
|
+
// ---- on-screen texts (bridge 0.10.0) ------------------------------------------------
|
|
25458
|
+
/** The languages with on-screen texts in the game, and whether the base game and Phantom Liberty have them. */
|
|
25459
|
+
locLanguages() {
|
|
25460
|
+
return this.call("loc.languages");
|
|
25461
|
+
}
|
|
25462
|
+
/**
|
|
25463
|
+
* On-screen texts: "LocKey#123" or a number finds that LocKey; otherwise every word must be in the
|
|
25464
|
+
* secondary key or a text. With projectDir, the project's onscreens entries win over the game's.
|
|
25465
|
+
*/
|
|
25466
|
+
locSearch(query, language = "en-us", limit = 200, projectDir) {
|
|
25467
|
+
return this.call("loc.search", { query, language, limit, projectDir: projectDir ?? null });
|
|
25468
|
+
}
|
|
25469
|
+
/** Entries by LocKey number or secondary key, with or without "LocKey#"; null where there is no text. */
|
|
25470
|
+
locGet(keys, language = "en-us", projectDir) {
|
|
25471
|
+
return this.call("loc.get", { keys, language, projectDir: projectDir ?? null });
|
|
25472
|
+
}
|
|
25473
|
+
/** The project's onscreens files for a language: those its .xl files name, then any in source/archive under <language>\onscreens. */
|
|
25474
|
+
locProjectFiles(projectDir, language = "en-us") {
|
|
25475
|
+
return this.call("loc.projectFiles", { projectDir, language });
|
|
25476
|
+
}
|
|
25477
|
+
/** The project's onscreens file for a language, written empty (and named in an .xl) when it has none. */
|
|
25478
|
+
locNewFile(projectDir, modName, language = "en-us") {
|
|
25479
|
+
return this.call("loc.newFile", { projectDir, modName, language });
|
|
25480
|
+
}
|
|
25453
25481
|
// ---- cr2w document sessions (bridge 0.5.0) -------------------------------------------
|
|
25454
25482
|
/** Parses a file once and keeps it open in the bridge. A game path opens read-only. */
|
|
25455
25483
|
openDocument(source) {
|
|
@@ -25498,7 +25526,7 @@ var BridgeClient = class extends import_node_events.EventEmitter {
|
|
|
25498
25526
|
};
|
|
25499
25527
|
|
|
25500
25528
|
// src/server.ts
|
|
25501
|
-
var Version = "0.
|
|
25529
|
+
var Version = "0.3.0";
|
|
25502
25530
|
function arg(name) {
|
|
25503
25531
|
const i = process.argv.indexOf(name);
|
|
25504
25532
|
return i >= 0 ? process.argv[i + 1] : void 0;
|
|
@@ -25645,12 +25673,13 @@ tool("project_export", "Export a game file under a project's source/archive to t
|
|
|
25645
25673
|
meshBinary: external_exports.boolean().optional().describe("Meshes as .glb (true, default) or .gltf."),
|
|
25646
25674
|
meshMaterials: external_exports.boolean().optional().describe("Export a mesh's materials and textures too; default true.")
|
|
25647
25675
|
}, (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,
|
|
25676
|
+
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
25677
|
file: external_exports.string().describe("The raw file, under rawDir."),
|
|
25650
25678
|
rawDir: external_exports.string().describe("The project's source/raw folder."),
|
|
25651
25679
|
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
|
-
|
|
25680
|
+
keep: external_exports.boolean().optional().describe("Keep the replaced file's settings; default true."),
|
|
25681
|
+
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.")
|
|
25682
|
+
}, (a, b) => b.importFile(a.file, a.rawDir, a.archiveDir, a.keep ?? true, a.template));
|
|
25654
25683
|
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
25684
|
dir: external_exports.string().describe("The folder to pack, laid out as game paths (base/..., ep1/...)."),
|
|
25656
25685
|
outDir: external_exports.string().describe("Where the archive goes, usually packed/archive/pc/mod."),
|
|
@@ -25693,6 +25722,19 @@ tool("tweakdb_search", `TweakDB records whose path contains every word, any case
|
|
|
25693
25722
|
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
25723
|
ids: external_exports.array(external_exports.string())
|
|
25695
25724
|
}, (a, b) => b.tweakNames(a.ids));
|
|
25725
|
+
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.";
|
|
25726
|
+
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());
|
|
25727
|
+
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}`, {
|
|
25728
|
+
query: external_exports.string().describe("LocKey#123, a secondary key, or words such as 'militech pistol'."),
|
|
25729
|
+
language: external_exports.string().optional(),
|
|
25730
|
+
limit: external_exports.number().optional().describe("Most results to return (default 200)."),
|
|
25731
|
+
projectDir: external_exports.string().optional().describe("A REDcode or WolvenKit project folder whose own texts should count.")
|
|
25732
|
+
}, (a, b) => b.locSearch(a.query, a.language ?? "en-us", a.limit ?? 200, a.projectDir));
|
|
25733
|
+
tool("loc_get", `The texts for LocKeys, by number or secondary key, with or without 'LocKey#'; null where there is none. ${LocRules}`, {
|
|
25734
|
+
keys: external_exports.array(external_exports.string()).describe("Such as ['LocKey#12345', 'Gameplay-Devices-Computers-Common-NewsFeed']."),
|
|
25735
|
+
language: external_exports.string().optional(),
|
|
25736
|
+
projectDir: external_exports.string().optional()
|
|
25737
|
+
}, (a, b) => b.locGet(a.keys, a.language ?? "en-us", a.projectDir));
|
|
25696
25738
|
var soundCache = path3.join(os.tmpdir(), "redcode-mcp", "sounds");
|
|
25697
25739
|
var soundIndex;
|
|
25698
25740
|
async function gameSounds(b) {
|
|
@@ -25713,6 +25755,10 @@ tool("sound_events", "Find the game's sound effect events by name or tag (words
|
|
|
25713
25755
|
const hits = events.events.filter((e) => groups.has(e.hash) && words.every((w) => `${e.name} ${e.tags.join(" ")}`.toLowerCase().includes(w)));
|
|
25714
25756
|
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
25757
|
});
|
|
25758
|
+
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.", {
|
|
25759
|
+
wavFiles: external_exports.array(external_exports.string()).describe("Absolute paths of <hash>.wav files."),
|
|
25760
|
+
archiveDir: external_exports.string().describe("The project's source/archive folder.")
|
|
25761
|
+
}, (a, b) => b.importWavs(a.wavFiles, a.archiveDir));
|
|
25716
25762
|
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
25763
|
event: external_exports.string().optional().describe("An event name, such as w_gun_npc_nova_reload, or its decimal hash."),
|
|
25718
25764
|
sounds: external_exports.array(external_exports.number()).optional().describe("Or sound hashes."),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgemodders/redcode-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
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
5
|
"license": "MIT",
|
|
6
6
|
"author": "Edgemodders",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "https://github.com/Edgemodders/REDcode",
|
|
13
|
+
"url": "git+https://github.com/Edgemodders/REDcode.git",
|
|
14
14
|
"directory": "packages/redcode-mcp"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|