wowdump 0.2.0 → 0.3.1

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.
Files changed (47) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +56 -118
  3. package/dist/agent.js +5 -8
  4. package/dist/cli.js +497 -0
  5. package/dist/discovery.js +1 -12
  6. package/dist/dry-run.js +0 -2
  7. package/dist/focused-session.js +61 -1329
  8. package/dist/frida-runtime.js +51 -73
  9. package/dist/frida-worker.js +100 -0
  10. package/dist/ghidra.js +769 -0
  11. package/dist/main.js +66 -0
  12. package/dist/processes.js +2 -5
  13. package/dist/reader-broker.js +460 -0
  14. package/dist/reader-client.js +1 -0
  15. package/dist/reader-main.js +67 -0
  16. package/dist/session.js +17 -120
  17. package/dist/toolchain.js +594 -0
  18. package/dist/windows-launcher.js +207 -0
  19. package/dist/windows-reader.js +102 -0
  20. package/dist/wow-analysis.js +211 -236
  21. package/package.json +18 -35
  22. package/skills/wowdump/SKILL.md +15 -0
  23. package/skills/wowdump/commands.md +44 -0
  24. package/dist/analysis-path.js +0 -38
  25. package/dist/analysis-process-log.js +0 -146
  26. package/dist/broker-client.js +0 -411
  27. package/dist/broker-codec.js +0 -148
  28. package/dist/broker-core.js +0 -1045
  29. package/dist/broker-gateway.js +0 -447
  30. package/dist/broker-ledger.js +0 -196
  31. package/dist/broker-main.js +0 -291
  32. package/dist/broker-protocol.js +0 -119
  33. package/dist/broker-runtime.js +0 -1283
  34. package/dist/broker-server.js +0 -466
  35. package/dist/build-bundle-loader.js +0 -183
  36. package/dist/build-bundle.js +0 -11
  37. package/dist/focus-errors.js +0 -63
  38. package/dist/focus-service.js +0 -1855
  39. package/dist/mcp-main.js +0 -51
  40. package/dist/mcp.js +0 -924
  41. package/dist/process-log-lock.js +0 -181
  42. package/dist/runtime-config.js +0 -399
  43. package/resources/builds/retail/12.0.7.68974/build-profile.json +0 -290
  44. package/resources/builds/retail/12.0.7.68974/data-sources.json +0 -1633
  45. package/resources/builds/retail/12.0.7.68974/lua-targets.jsonl +0 -5130
  46. package/resources/builds/retail/12.0.7.68974/manifest.json +0 -63
  47. package/resources/builds/retail/12.0.7.68974/signatures.json +0 -260
package/dist/mcp.js DELETED
@@ -1,924 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { fromJsonSchema, McpServer } from "@modelcontextprotocol/server";
3
- import { buildAdapterRegistry } from "./adapters.js";
4
- import { createDryRunReport } from "./dry-run.js";
5
- import { readErrorLog } from "./error-log.js";
6
- import { discoverInstalls, discoverInstallsAcrossRoots } from "./discovery.js";
7
- import { listWowProcesses } from "./processes.js";
8
- import { createWowAnalysisService } from "./wow-analysis.js";
9
- export const MCP_MODERN_PROTOCOL_VERSION = "2026-07-28";
10
- export const MCP_SERVER_NAME = "wowdump";
11
- export const MCP_SERVER_VERSION = "0.2.0";
12
- export const LATEST_ERRORS_RESOURCE_URI = "wow-lua-errors://latest";
13
- const ERROR_EVENT_SCHEMA = {
14
- type: "object",
15
- additionalProperties: false,
16
- properties: {
17
- schema: { const: 1 },
18
- type: { const: "lua_error" },
19
- timestamp: { type: "number" },
20
- pid: { type: "number" },
21
- threadId: { type: "number" },
22
- buildKey: { type: "string" },
23
- flavor: { type: "string" },
24
- addon: { type: "string" },
25
- message: { type: "string" },
26
- stack: { type: "string" },
27
- sourceAddress: { type: "string" }
28
- },
29
- required: ["schema", "type", "timestamp", "pid", "buildKey", "flavor", "message"]
30
- };
31
- const ERROR_LOG_RESULT_SCHEMA = {
32
- type: "object",
33
- additionalProperties: false,
34
- properties: {
35
- events: { type: "array", items: ERROR_EVENT_SCHEMA },
36
- diagnostics: {
37
- type: "array",
38
- items: {
39
- type: "object",
40
- additionalProperties: false,
41
- properties: {
42
- line: { type: "number" },
43
- code: { type: "string", enum: ["invalid_json", "invalid_event"] },
44
- message: { type: "string" }
45
- },
46
- required: ["line", "code", "message"]
47
- }
48
- }
49
- },
50
- required: ["events", "diagnostics"]
51
- };
52
- const ERROR_LOG_QUERY_SCHEMA = {
53
- type: "object",
54
- additionalProperties: false,
55
- properties: {
56
- limit: { type: "integer", minimum: 1, maximum: 1000 },
57
- pid: { type: "integer", minimum: 1 },
58
- buildKey: { type: "string", minLength: 1 },
59
- flavor: { type: "string", minLength: 1 },
60
- addon: { type: "string", minLength: 1 }
61
- }
62
- };
63
- const JSON_VALUE_SCHEMA = {
64
- anyOf: [
65
- { type: "null" },
66
- { type: "boolean" },
67
- { type: "number" },
68
- { type: "string" },
69
- { type: "array", items: {} },
70
- { type: "object", additionalProperties: true }
71
- ]
72
- };
73
- const MUTATION_CONTRACT_SCHEMA = {
74
- type: "object",
75
- additionalProperties: false,
76
- properties: {
77
- mutationPlanId: { type: "string", minLength: 1 },
78
- confirmation: {
79
- type: "object",
80
- additionalProperties: false,
81
- properties: {
82
- clientId: { type: "string", minLength: 1 },
83
- confirmedAt: { type: "string", minLength: 1 },
84
- nonce: { type: "string", minLength: 1 },
85
- irreversibleAcknowledged: { type: "boolean" }
86
- },
87
- required: ["clientId", "confirmedAt", "nonce"]
88
- },
89
- target: {
90
- type: "object",
91
- additionalProperties: false,
92
- properties: {
93
- pid: { type: "integer", minimum: 1 },
94
- buildKey: { anyOf: [{ type: "string", minLength: 1 }, { type: "null" }] }
95
- },
96
- required: ["pid", "buildKey"]
97
- },
98
- expectedEffect: JSON_VALUE_SCHEMA,
99
- rollback: {
100
- type: "object",
101
- additionalProperties: false,
102
- properties: {
103
- mode: { type: "string", enum: ["reversible", "compensating", "irreversible"] },
104
- plan: JSON_VALUE_SCHEMA,
105
- deadline: { type: "string", minLength: 1 },
106
- successOracle: JSON_VALUE_SCHEMA,
107
- irreversibleReason: { type: "string", minLength: 1 }
108
- },
109
- required: ["mode"]
110
- },
111
- evidencePlan: {
112
- type: "object",
113
- additionalProperties: false,
114
- properties: {
115
- before: JSON_VALUE_SCHEMA,
116
- after: JSON_VALUE_SCHEMA,
117
- rollback: JSON_VALUE_SCHEMA
118
- },
119
- required: ["before", "after"]
120
- },
121
- audit: {
122
- type: "object",
123
- additionalProperties: false,
124
- properties: {
125
- actor: { type: "string", minLength: 1 },
126
- reason: { type: "string", minLength: 1 },
127
- requestId: { type: "string", minLength: 1 }
128
- },
129
- required: ["actor", "reason", "requestId"]
130
- }
131
- },
132
- required: ["mutationPlanId", "confirmation", "target", "expectedEffect", "rollback", "evidencePlan", "audit"]
133
- };
134
- const LAUNCH_CONTRACT_SCHEMA = {
135
- type: "object",
136
- additionalProperties: false,
137
- properties: {
138
- mutationPlanId: { type: "string", minLength: 1 },
139
- confirmation: MUTATION_CONTRACT_SCHEMA.properties?.confirmation ?? { type: "object" },
140
- launchSpec: {
141
- type: "object",
142
- additionalProperties: false,
143
- properties: {
144
- executable: { type: "string", minLength: 1 },
145
- argv: { type: "array", items: { type: "string" }, maxItems: 256 },
146
- cwd: { type: "string", minLength: 1 },
147
- environmentDigest: { type: "string", pattern: "^[A-Fa-f0-9]{64}$" },
148
- suspended: { type: "boolean" }
149
- },
150
- required: ["executable", "argv", "cwd", "environmentDigest", "suspended"]
151
- },
152
- expectedBuildKey: { anyOf: [{ type: "string", minLength: 1 }, { type: "null" }] },
153
- expectedEffect: MUTATION_CONTRACT_SCHEMA.properties?.expectedEffect ?? JSON_VALUE_SCHEMA,
154
- rollback: MUTATION_CONTRACT_SCHEMA.properties?.rollback ?? { type: "object" },
155
- evidencePlan: MUTATION_CONTRACT_SCHEMA.properties?.evidencePlan ?? { type: "object" },
156
- audit: MUTATION_CONTRACT_SCHEMA.properties?.audit ?? { type: "object" }
157
- },
158
- required: ["mutationPlanId", "confirmation", "launchSpec", "expectedEffect", "rollback", "evidencePlan", "audit"]
159
- };
160
- /**
161
- * One MCP entry point for the Frida host API and GumJS surface. The explicit
162
- * operations cover the common host-side commands; `script` and `script_load`
163
- * keep the complete Frida JavaScript API available for build-specific work.
164
- */
165
- const FRIDA_COMMAND_SCHEMA = {
166
- type: "object",
167
- additionalProperties: false,
168
- properties: {
169
- operation: {
170
- type: "string",
171
- enum: [
172
- "devices",
173
- "host_call",
174
- "processes",
175
- "applications",
176
- "spawn",
177
- "resume",
178
- "kill",
179
- "attach",
180
- "detach",
181
- "session_status",
182
- "modules",
183
- "exports",
184
- "ranges",
185
- "read_memory",
186
- "write_memory",
187
- "protect_memory",
188
- "scan_memory",
189
- "script",
190
- "script_load",
191
- "script_call",
192
- "script_unload",
193
- "compile_script",
194
- "handle_release"
195
- ]
196
- },
197
- deviceId: { type: "string", minLength: 1 },
198
- requestId: { type: "string", minLength: 1 },
199
- pid: { type: "integer", minimum: 1 },
200
- buildKey: { type: "string", minLength: 1 },
201
- flavor: { type: "string", minLength: 1 },
202
- sessionId: { type: "string", minLength: 1 },
203
- scriptId: { type: "string", minLength: 1 },
204
- resourceId: { type: "string", minLength: 1 },
205
- module: { type: "string", minLength: 1 },
206
- protection: { type: "string", minLength: 1 },
207
- address: { type: "string", minLength: 1 },
208
- size: { type: "integer", minimum: 1, maximum: 16777216 },
209
- bytesHex: { type: "string", minLength: 2, maxLength: 33554432 },
210
- hex: { type: "string", minLength: 2, maxLength: 33554432 },
211
- pattern: { type: "string", minLength: 1 },
212
- source: { type: "string", minLength: 1, maxLength: 4194304 },
213
- exportName: { type: "string", minLength: 1 },
214
- args: { type: "array", items: {} },
215
- program: { type: "string", minLength: 1 },
216
- argv: { type: "array", items: { type: "string" } },
217
- options: { type: "object", additionalProperties: true },
218
- timeoutMs: { type: "integer", minimum: 1, maximum: 120000 },
219
- persist: { type: "boolean" },
220
- coalesce: { type: "boolean" },
221
- scope: { type: "string", enum: ["api", "device", "session"] },
222
- method: { type: "string", minLength: 1 },
223
- mutation: MUTATION_CONTRACT_SCHEMA,
224
- launch: LAUNCH_CONTRACT_SCHEMA
225
- },
226
- required: ["operation"],
227
- allOf: [
228
- {
229
- if: { properties: { operation: { const: "handle_release" } } },
230
- then: { required: ["resourceId"] }
231
- }
232
- ]
233
- };
234
- const FRIDA_COMMAND_RESULT_SCHEMA = {
235
- type: "object",
236
- additionalProperties: true
237
- };
238
- const BUILD_TARGET_PROPERTIES = {
239
- pid: { type: "integer", minimum: 1 },
240
- buildKey: { type: "string", minLength: 1 }
241
- };
242
- const TRACE_SELECTOR_PROPERTIES = {
243
- APIs: { type: "array", items: { type: "string", minLength: 1 }, maxItems: 512 },
244
- apis: { type: "array", items: { type: "string", minLength: 1 }, maxItems: 512 },
245
- namespace: { type: "string", minLength: 1 },
246
- glob: { type: "string", minLength: 1 },
247
- rva: {},
248
- all: { type: "boolean" }
249
- };
250
- const WOW_LUA_TRACE_START_SCHEMA = {
251
- type: "object",
252
- additionalProperties: false,
253
- properties: {
254
- ...BUILD_TARGET_PROPERTIES,
255
- ...TRACE_SELECTOR_PROPERTIES,
256
- captureArgs: { type: "boolean" },
257
- captureReturns: { type: "boolean" },
258
- captureNativeArguments: { type: "boolean" },
259
- tableDepth: { type: "integer", minimum: 0, maximum: 8 },
260
- maxItems: { type: "integer", minimum: 1, maximum: 1024 },
261
- maxStringBytes: { type: "integer", minimum: 1, maximum: 1048576 },
262
- maxStackValues: { type: "integer", minimum: 1, maximum: 4096 },
263
- maxInvocationDepth: { type: "integer", minimum: 1, maximum: 1024 },
264
- maxDecodeMs: { type: "integer", minimum: 1, maximum: 1000 },
265
- maxEvents: { type: "integer", minimum: 1, maximum: 100000 },
266
- maxHooks: { type: "integer", minimum: 1, maximum: 512 },
267
- sampling: { type: "number", exclusiveMinimum: 0, maximum: 1 },
268
- durationMs: { type: "integer", minimum: 0, maximum: 86400000 },
269
- allowTableIteration: { type: "boolean" },
270
- staleInvocationMs: { type: "integer", minimum: 100, maximum: 600000 }
271
- },
272
- required: ["buildKey"]
273
- };
274
- const WOW_CPP_TRACE_START_SCHEMA = {
275
- type: "object",
276
- additionalProperties: false,
277
- properties: {
278
- ...BUILD_TARGET_PROPERTIES,
279
- ...TRACE_SELECTOR_PROPERTIES,
280
- clusterIds: { type: "array", items: { type: "string", minLength: 1 }, maxItems: 256 },
281
- captureArguments: { type: "boolean" },
282
- captureReturn: { type: "boolean" },
283
- argumentCount: { type: "integer", minimum: 0, maximum: 32 },
284
- maxEvents: { type: "integer", minimum: 1, maximum: 100000 },
285
- maxHooks: { type: "integer", minimum: 1, maximum: 256 },
286
- sampling: { type: "number", exclusiveMinimum: 0, maximum: 1 },
287
- durationMs: { type: "integer", minimum: 0, maximum: 86400000 }
288
- },
289
- required: ["buildKey"]
290
- };
291
- const WOW_TRACE_READ_SCHEMA = {
292
- type: "object",
293
- additionalProperties: false,
294
- properties: {
295
- afterSeq: { type: "integer", minimum: 0 },
296
- limit: { type: "integer", minimum: 1, maximum: 10000 }
297
- }
298
- };
299
- const WOW_TRACE_STOP_SCHEMA = {
300
- type: "object",
301
- additionalProperties: false,
302
- properties: { reason: { type: "string", minLength: 1, maxLength: 256 } }
303
- };
304
- const EMPTY_INPUT_SCHEMA = {
305
- type: "object",
306
- additionalProperties: false,
307
- properties: {}
308
- };
309
- const DISCOVERY_RESULT_SCHEMA = {
310
- type: "object",
311
- additionalProperties: true
312
- };
313
- const WOW_DATA_SOURCE_LIST_SCHEMA = {
314
- type: "object",
315
- additionalProperties: false,
316
- properties: {
317
- buildKey: { type: "string", minLength: 1 },
318
- status: { type: "string", minLength: 1 },
319
- readerStatus: { type: "string", minLength: 1 },
320
- system: { type: "string", minLength: 1 },
321
- offset: { type: "integer", minimum: 0 },
322
- limit: { type: "integer", minimum: 1, maximum: 10000 }
323
- },
324
- required: ["buildKey"]
325
- };
326
- const WOW_DATA_SOURCE_DESCRIBE_SCHEMA = {
327
- type: "object",
328
- additionalProperties: false,
329
- properties: {
330
- buildKey: { type: "string", minLength: 1 },
331
- dataSourceId: { type: "string", minLength: 1 }
332
- },
333
- required: ["buildKey", "dataSourceId"]
334
- };
335
- const WOW_DATA_READ_SCHEMA = {
336
- type: "object",
337
- additionalProperties: false,
338
- properties: {
339
- ...BUILD_TARGET_PROPERTIES,
340
- dataSourceId: { type: "string", minLength: 1 },
341
- limit: { type: "integer", minimum: 1, maximum: 10000 }
342
- },
343
- required: ["buildKey", "dataSourceId"]
344
- };
345
- const WOW_BUILD_PROFILE_VALIDATE_SCHEMA = {
346
- type: "object",
347
- additionalProperties: false,
348
- properties: {
349
- ...BUILD_TARGET_PROPERTIES,
350
- maxChecks: { type: "integer", minimum: 1, maximum: 512 }
351
- },
352
- required: ["buildKey"]
353
- };
354
- const WOW_ANALYSIS_COVERAGE_SCHEMA = {
355
- type: "object",
356
- additionalProperties: false,
357
- properties: { buildKey: { type: "string", minLength: 1 } },
358
- required: ["buildKey"]
359
- };
360
- const WOW_ANALYSIS_CHECKPOINT_SCHEMA = {
361
- type: "object",
362
- additionalProperties: false,
363
- properties: {
364
- buildKey: { type: "string", minLength: 1 },
365
- label: { type: "string", minLength: 1, maxLength: 256 }
366
- },
367
- required: ["buildKey"]
368
- };
369
- const FOCUS_SELECTOR_SCHEMA = {
370
- type: "object",
371
- additionalProperties: false,
372
- properties: {
373
- combine: { type: "string", enum: ["all", "any"] },
374
- apis: { type: "array", items: { type: "string", minLength: 1 }, maxItems: 256 },
375
- namespaces: { type: "array", items: { type: "string", minLength: 1 }, maxItems: 256 },
376
- globs: { type: "array", items: { type: "string", minLength: 1 }, maxItems: 256 },
377
- rvas: { type: "array", items: { type: "string", minLength: 1 }, maxItems: 256 },
378
- dataSourceIds: { type: "array", items: { type: "string", minLength: 1 }, maxItems: 256 },
379
- objectIds: { type: "array", items: { type: "string", minLength: 1 }, maxItems: 256 },
380
- ranges: { type: "array", items: { type: "object", additionalProperties: false, properties: { rva: { type: "string", pattern: "^0x(?:0|[1-9A-F][0-9A-F]*)$" }, size: { type: "integer", minimum: 1, maximum: 2147483647 } }, required: ["rva", "size"] }, maxItems: 256 },
381
- maxTargets: { type: "integer", minimum: 1, maximum: 256 }
382
- }
383
- };
384
- const ENVIRONMENT_DESCRIPTOR_SCHEMA = {
385
- type: "object",
386
- additionalProperties: true,
387
- minProperties: 1
388
- };
389
- const TRIGGER_PLAN_SCHEMA = {
390
- type: "object",
391
- additionalProperties: true,
392
- minProperties: 1
393
- };
394
- const FOCUS_START_SCHEMA = {
395
- type: "object",
396
- additionalProperties: false,
397
- properties: {
398
- pid: { type: "integer", minimum: 1 },
399
- buildKey: { type: "string", minLength: 1 },
400
- targetKind: { type: "string", enum: ["lua_wrapper", "cpp_function", "data_source", "object", "address_range"] },
401
- selector: FOCUS_SELECTOR_SCHEMA,
402
- environmentDescriptor: ENVIRONMENT_DESCRIPTOR_SCHEMA,
403
- triggerPlan: TRIGGER_PLAN_SCHEMA,
404
- captureArgs: { type: "boolean" },
405
- captureReturns: { type: "boolean" },
406
- captureCallStack: { type: "boolean" },
407
- captureMemoryWrites: { type: "boolean" },
408
- captureObjectDiff: { type: "boolean" },
409
- snapshotIntervalMs: { type: "integer", minimum: 0, maximum: 60000 },
410
- maxEvents: { type: "integer", minimum: 1, maximum: 100000 },
411
- maxBytes: { type: "integer", minimum: 1, maximum: 67108864 },
412
- durationMs: { type: "integer", minimum: 0, maximum: 86400000 },
413
- sampling: { type: "number", exclusiveMinimum: 0, maximum: 1 },
414
- sessionId: { type: "string", minLength: 1 }
415
- },
416
- required: ["pid", "buildKey", "targetKind", "selector", "environmentDescriptor", "triggerPlan"]
417
- };
418
- const WATCH_START_SCHEMA = {
419
- ...FOCUS_START_SCHEMA,
420
- properties: {
421
- ...(FOCUS_START_SCHEMA.properties ?? {}),
422
- targetKind: { type: "string", enum: ["data_source", "object", "address_range"] }
423
- }
424
- };
425
- const FOCUS_IDENTITY_SCHEMA = {
426
- type: "object",
427
- additionalProperties: false,
428
- properties: {
429
- sessionId: { type: "string", minLength: 1 },
430
- pid: { type: "integer", minimum: 1 },
431
- buildKey: { type: "string", minLength: 1 }
432
- },
433
- required: ["sessionId", "pid", "buildKey"]
434
- };
435
- const FOCUS_READ_SCHEMA = {
436
- ...FOCUS_IDENTITY_SCHEMA,
437
- properties: {
438
- ...(FOCUS_IDENTITY_SCHEMA.properties ?? {}),
439
- afterSeq: { type: "integer", minimum: 0 },
440
- cursor: { type: "integer", minimum: 0 },
441
- limit: { type: "integer", minimum: 1, maximum: 10000 }
442
- }
443
- };
444
- const FOCUS_STOP_SCHEMA = {
445
- ...FOCUS_IDENTITY_SCHEMA,
446
- properties: {
447
- ...(FOCUS_IDENTITY_SCHEMA.properties ?? {}),
448
- reason: { type: "string", minLength: 1, maxLength: 256 }
449
- }
450
- };
451
- const FOCUS_CHECKPOINT_SCHEMA = {
452
- ...FOCUS_IDENTITY_SCHEMA,
453
- properties: {
454
- ...(FOCUS_IDENTITY_SCHEMA.properties ?? {}),
455
- triggerPhase: { type: "string", enum: ["before", "during", "after"] },
456
- phaseIndex: { type: "integer", minimum: 0 },
457
- userAction: { type: "string", minLength: 1, maxLength: 4096 },
458
- snapshotNow: { type: "boolean" }
459
- },
460
- required: ["sessionId", "pid", "buildKey", "triggerPhase", "phaseIndex", "userAction"]
461
- };
462
- const READ_ONLY_ANNOTATIONS = {
463
- readOnlyHint: true,
464
- destructiveHint: false,
465
- idempotentHint: true,
466
- openWorldHint: false
467
- };
468
- const READ_WRITE_ANNOTATIONS = {
469
- readOnlyHint: false,
470
- destructiveHint: true,
471
- idempotentHint: false,
472
- openWorldHint: false
473
- };
474
- function textResult(value, isError = false) {
475
- return {
476
- content: [{ type: "text", text: JSON.stringify(value) }],
477
- structuredContent: value,
478
- ...(isError ? { isError: true } : {})
479
- };
480
- }
481
- function errorText(error) {
482
- return error instanceof Error ? error.message : String(error);
483
- }
484
- export function createWowBuildFridaMcpServer(options) {
485
- const registry = options.adapterRegistry ?? buildAdapterRegistry;
486
- const discover = options.discover ?? discoverInstalls;
487
- const listProcesses = options.listProcesses ?? listWowProcesses;
488
- const readLog = options.readLog ?? readErrorLog;
489
- const fridaRuntime = options.fridaRuntime ?? createUnavailableExecutor();
490
- const analysisService = options.analysisService ?? createWowAnalysisService({
491
- executor: fridaRuntime,
492
- profileRoot: options.profileRoot ?? options.analysisDir ?? requiredProfileRoot()
493
- });
494
- const focusService = options.focusService ?? createUnavailableFocusExecutor();
495
- const gameRoots = normalizeGameRoots(options);
496
- const discoverAll = () => discoverInstallsAcrossRoots(gameRoots, discover);
497
- const server = new McpServer({ name: MCP_SERVER_NAME, version: MCP_SERVER_VERSION }, {
498
- instructions: "WoW build-aware Frida analysis. frida_command exposes host Frida operations and GumJS scripts, including explicit memory writes and process-control operations. Select a PID/buildKey for target operations; build adapters add verified build context.",
499
- capabilities: { tools: {}, resources: {} }
500
- });
501
- const querySchema = fromJsonSchema(ERROR_LOG_QUERY_SCHEMA);
502
- const resultSchema = fromJsonSchema(ERROR_LOG_RESULT_SCHEMA);
503
- const fridaCommandSchema = fromJsonSchema(FRIDA_COMMAND_SCHEMA);
504
- const fridaResultSchema = fromJsonSchema(FRIDA_COMMAND_RESULT_SCHEMA);
505
- const discoveryResultSchema = fromJsonSchema(DISCOVERY_RESULT_SCHEMA);
506
- server.registerTool("wow_install_list", {
507
- title: "List WoW installations",
508
- description: "List configured game roots and discovered WoW installations without attaching.",
509
- inputSchema: fromJsonSchema(EMPTY_INPUT_SCHEMA),
510
- outputSchema: discoveryResultSchema,
511
- annotations: READ_ONLY_ANNOTATIONS
512
- }, async () => textResult({
513
- gameRoots,
514
- installs: discoverAll().map(install => ({
515
- root: install.root,
516
- executable: install.executable,
517
- flavor: install.flavor,
518
- buildKey: install.build.buildKey,
519
- build: install.build
520
- }))
521
- }));
522
- server.registerTool("wow_build_list", {
523
- title: "List WoW builds",
524
- description: "List unique builds discovered across all configured game roots without attaching.",
525
- inputSchema: fromJsonSchema(EMPTY_INPUT_SCHEMA),
526
- outputSchema: discoveryResultSchema,
527
- annotations: READ_ONLY_ANNOTATIONS
528
- }, async () => {
529
- const builds = new Map();
530
- for (const install of discoverAll()) {
531
- const current = builds.get(install.build.buildKey);
532
- if (current) {
533
- if (!current.flavors.includes(install.flavor))
534
- current.flavors.push(install.flavor);
535
- if (!current.executables.includes(install.executable))
536
- current.executables.push(install.executable);
537
- }
538
- else {
539
- builds.set(install.build.buildKey, {
540
- buildKey: install.build.buildKey,
541
- build: install.build,
542
- flavors: [install.flavor],
543
- executables: [install.executable]
544
- });
545
- }
546
- }
547
- return textResult({ builds: [...builds.values()] });
548
- });
549
- server.registerTool("wow_target_list", {
550
- title: "List live WoW targets",
551
- description: "List live matched WoW processes and explicitly report discovered installations with no matching process; never attaches.",
552
- inputSchema: fromJsonSchema(EMPTY_INPUT_SCHEMA),
553
- outputSchema: discoveryResultSchema,
554
- annotations: READ_ONLY_ANNOTATIONS
555
- }, async () => {
556
- const installs = discoverAll();
557
- try {
558
- const processes = await listProcesses(installs);
559
- const liveExecutables = new Set(processes.map(process => resolve(process.install.executable).toLowerCase()));
560
- return textResult({
561
- targets: processes.map(process => ({
562
- status: "matched",
563
- pid: process.pid,
564
- path: process.executable,
565
- flavor: process.install.flavor,
566
- buildKey: process.install.build.buildKey,
567
- module: { name: "Wow.exe", path: process.executable },
568
- startTime: process.startTime ?? null
569
- })),
570
- unmatched: installs.filter(install => !liveExecutables.has(resolve(install.executable).toLowerCase())).map(install => ({
571
- status: "not_running",
572
- pid: null,
573
- path: install.executable,
574
- flavor: install.flavor,
575
- buildKey: install.build.buildKey,
576
- module: { name: "Wow.exe", path: install.executable }
577
- }))
578
- });
579
- }
580
- catch (error) {
581
- return textResult({ targets: [], unmatched: installs.map(install => ({
582
- status: "scan_failed",
583
- pid: null,
584
- path: install.executable,
585
- flavor: install.flavor,
586
- buildKey: install.build.buildKey,
587
- module: { name: "Wow.exe", path: install.executable }
588
- })), processScanError: errorText(error) });
589
- }
590
- });
591
- server.registerTool("list_lua_errors", {
592
- title: "List Lua errors",
593
- description: "Read and filter existing JSONL Lua error events.",
594
- inputSchema: querySchema,
595
- outputSchema: resultSchema,
596
- annotations: READ_ONLY_ANNOTATIONS
597
- }, async (query = {}) => {
598
- try {
599
- return textResult(await readLog(options.errorsFile, query));
600
- }
601
- catch (error) {
602
- return textResult({ error: errorText(error) }, true);
603
- }
604
- });
605
- server.registerTool("bridge_status", {
606
- title: "Bridge status",
607
- description: "Report discovered installs, live PIDs, adapter state, and read-only tiers.",
608
- annotations: READ_ONLY_ANNOTATIONS
609
- }, async () => {
610
- const installs = discoverAll();
611
- let processes = [];
612
- let processScanError;
613
- try {
614
- processes = await listProcesses(installs);
615
- }
616
- catch (error) {
617
- processScanError = errorText(error);
618
- }
619
- const report = createDryRunReport(installs, processes, registry);
620
- return textResult({
621
- protocolVersion: MCP_MODERN_PROTOCOL_VERSION,
622
- semanticReadOnly: true,
623
- fridaGateway: {
624
- tool: "frida_command",
625
- mutationCapable: true,
626
- buildAware: true
627
- },
628
- ...report,
629
- ...(processScanError ? { processScanError } : {})
630
- });
631
- });
632
- server.registerTool("frida_command", {
633
- title: "Run Frida command",
634
- description: "Run a build-aware Frida host or GumJS operation. Supports device/process discovery, host_call for the complete Node Frida surface, attach/detach, modules, exports, ranges, memory read/write/protect/scan, script loading/calls, compilation, spawn, resume, and kill.",
635
- inputSchema: fridaCommandSchema,
636
- outputSchema: fridaResultSchema,
637
- annotations: READ_WRITE_ANNOTATIONS
638
- }, async (request) => {
639
- try {
640
- const command = request;
641
- const target = await resolveFridaTarget(command, discover, listProcesses, gameRoots, registry);
642
- return textResult(await fridaRuntime.execute(command, target));
643
- }
644
- catch (error) {
645
- return textResult({ error: errorText(error) }, true);
646
- }
647
- });
648
- const analysisTools = [
649
- {
650
- name: "wow_lua_trace_start",
651
- title: "Start WoW Lua native trace",
652
- description: "Dynamically discover the selected WoW build, validate its module base and ABI catalog, then hook only explicitly selected Lua native wrappers.",
653
- schema: WOW_LUA_TRACE_START_SCHEMA,
654
- annotations: READ_WRITE_ANNOTATIONS
655
- },
656
- {
657
- name: "wow_lua_trace_read",
658
- title: "Read WoW Lua trace",
659
- description: "Read bounded Lua wrapper enter/leave events from the active trace ring.",
660
- schema: WOW_TRACE_READ_SCHEMA,
661
- annotations: READ_ONLY_ANNOTATIONS
662
- },
663
- {
664
- name: "wow_lua_trace_status",
665
- title: "Get WoW Lua trace status",
666
- description: "Report active Lua hooks, selected records, queued events and dynamic module base.",
667
- schema: EMPTY_INPUT_SCHEMA,
668
- annotations: READ_ONLY_ANNOTATIONS
669
- },
670
- {
671
- name: "wow_lua_trace_stop",
672
- title: "Stop WoW Lua trace",
673
- description: "Detach every Lua wrapper hook, dispose the script and release its owned Frida session.",
674
- schema: WOW_TRACE_STOP_SCHEMA,
675
- annotations: READ_WRITE_ANNOTATIONS
676
- },
677
- {
678
- name: "wow_cpp_trace_start",
679
- title: "Start indexed WoW C++ trace",
680
- description: "Trace explicitly selected business callees from the build callgraph with bounded hook and event limits.",
681
- schema: WOW_CPP_TRACE_START_SCHEMA,
682
- annotations: READ_WRITE_ANNOTATIONS
683
- },
684
- {
685
- name: "wow_cpp_trace_read",
686
- title: "Read WoW C++ trace",
687
- description: "Read bounded events from the active indexed C++ trace.",
688
- schema: WOW_TRACE_READ_SCHEMA,
689
- annotations: READ_ONLY_ANNOTATIONS
690
- },
691
- {
692
- name: "wow_cpp_trace_status",
693
- title: "Get WoW C++ trace status",
694
- description: "Report active C++ hooks, dynamic base, queue depth and dropped events.",
695
- schema: EMPTY_INPUT_SCHEMA,
696
- annotations: READ_ONLY_ANNOTATIONS
697
- },
698
- {
699
- name: "wow_cpp_trace_stop",
700
- title: "Stop WoW C++ trace",
701
- description: "Detach every indexed C++ hook, dispose the script and release its owned Frida session.",
702
- schema: WOW_TRACE_STOP_SCHEMA,
703
- annotations: READ_WRITE_ANNOTATIONS
704
- },
705
- {
706
- name: "wow_data_source_list",
707
- title: "List WoW data sources",
708
- description: "List build-profile data sources and their reader/static/live verification status.",
709
- schema: WOW_DATA_SOURCE_LIST_SCHEMA,
710
- annotations: READ_ONLY_ANNOTATIONS
711
- },
712
- {
713
- name: "wow_data_source_describe",
714
- title: "Describe WoW data source",
715
- description: "Describe one indexed data source, pointer chain, fields, invariants and evidence.",
716
- schema: WOW_DATA_SOURCE_DESCRIBE_SCHEMA,
717
- annotations: READ_ONLY_ANNOTATIONS
718
- },
719
- {
720
- name: "wow_data_read",
721
- title: "Read verified WoW data source",
722
- description: "Read a reader_ready data source by ID using only its build profile RVA, fields and validation rules.",
723
- schema: WOW_DATA_READ_SCHEMA,
724
- annotations: READ_ONLY_ANNOTATIONS
725
- },
726
- {
727
- name: "wow_build_profile_validate",
728
- title: "Validate WoW build profile",
729
- description: "Dynamically discover Wow.exe, verify build signatures at base plus RVA, and report address evidence.",
730
- schema: WOW_BUILD_PROFILE_VALIDATE_SCHEMA,
731
- annotations: READ_ONLY_ANNOTATIONS
732
- },
733
- {
734
- name: "wow_analysis_coverage",
735
- title: "Read WoW analysis coverage",
736
- description: "Summarize artifact presence and Lua, C++ data-root and non-Lua coverage for a build.",
737
- schema: WOW_ANALYSIS_COVERAGE_SCHEMA,
738
- annotations: READ_ONLY_ANNOTATIONS
739
- },
740
- {
741
- name: "wow_analysis_checkpoint",
742
- title: "Create WoW analysis checkpoint",
743
- description: "Return a recoverable coverage checkpoint and its suggested append-only process event.",
744
- schema: WOW_ANALYSIS_CHECKPOINT_SCHEMA,
745
- annotations: READ_ONLY_ANNOTATIONS
746
- }
747
- ];
748
- for (const definition of analysisTools) {
749
- const inputSchema = fromJsonSchema(definition.schema);
750
- server.registerTool(definition.name, {
751
- title: definition.title,
752
- description: definition.description,
753
- inputSchema,
754
- outputSchema: fridaResultSchema,
755
- annotations: definition.annotations
756
- }, async (input = {}) => {
757
- try {
758
- return textResult(await analysisService.invoke(definition.name, input));
759
- }
760
- catch (error) {
761
- return textResult({ error: errorText(error) }, true);
762
- }
763
- });
764
- }
765
- const focusTools = [
766
- { name: "wow_focus_start", title: "Start focused WoW trace", description: "Start a PID/build-bound target-scoped session with before/during/after evidence.", schema: FOCUS_START_SCHEMA, annotations: READ_WRITE_ANNOTATIONS },
767
- { name: "wow_watch_start", title: "Start focused WoW watch", description: "Watch an explicitly selected data source, object, or address range over a bounded window.", schema: WATCH_START_SCHEMA, annotations: READ_WRITE_ANNOTATIONS },
768
- { name: "wow_focus_read", title: "Read focused WoW trace", description: "Read immutable focused events after a cursor.", schema: FOCUS_READ_SCHEMA, annotations: READ_ONLY_ANNOTATIONS },
769
- { name: "wow_watch_read", title: "Read focused WoW watch", description: "Read immutable watch events after a cursor.", schema: FOCUS_READ_SCHEMA, annotations: READ_ONLY_ANNOTATIONS },
770
- { name: "wow_focus_status", title: "Focused WoW trace status", description: "Report focused session identity, targets, buffers, drops and cleanup state.", schema: FOCUS_IDENTITY_SCHEMA, annotations: READ_ONLY_ANNOTATIONS },
771
- { name: "wow_watch_status", title: "Focused WoW watch status", description: "Report watch session identity, targets, buffers, drops and cleanup state.", schema: FOCUS_IDENTITY_SCHEMA, annotations: READ_ONLY_ANNOTATIONS },
772
- { name: "wow_focus_pause", title: "Pause focused WoW trace", description: "Pause event capture and record a timeline gap.", schema: FOCUS_IDENTITY_SCHEMA, annotations: READ_WRITE_ANNOTATIONS },
773
- { name: "wow_focus_resume", title: "Resume focused WoW trace", description: "Revalidate the target and resume event capture.", schema: FOCUS_IDENTITY_SCHEMA, annotations: READ_WRITE_ANNOTATIONS },
774
- { name: "wow_focus_stop", title: "Stop focused WoW trace", description: "Capture after evidence and clean every session-owned resource.", schema: FOCUS_STOP_SCHEMA, annotations: READ_WRITE_ANNOTATIONS },
775
- { name: "wow_watch_stop", title: "Stop focused WoW watch", description: "Capture after evidence and clean every watch-owned resource.", schema: FOCUS_STOP_SCHEMA, annotations: READ_WRITE_ANNOTATIONS },
776
- { name: "wow_session_checkpoint", title: "Checkpoint focused WoW session", description: "Append a validated trigger-plan checkpoint without rewriting events.", schema: FOCUS_CHECKPOINT_SCHEMA, annotations: READ_WRITE_ANNOTATIONS }
777
- ];
778
- for (const definition of focusTools) {
779
- const inputSchema = fromJsonSchema(definition.schema);
780
- server.registerTool(definition.name, {
781
- title: definition.title,
782
- description: definition.description,
783
- inputSchema,
784
- outputSchema: fridaResultSchema,
785
- annotations: definition.annotations
786
- }, async (input = {}) => {
787
- try {
788
- return textResult(await focusService.invoke(definition.name, input));
789
- }
790
- catch (error) {
791
- return textResult({ error: errorText(error) }, true);
792
- }
793
- });
794
- }
795
- const brokerStatusSchema = EMPTY_INPUT_SCHEMA;
796
- const brokerStatus = async () => {
797
- const installs = discoverAll();
798
- const processes = await listProcesses(installs).catch(() => []);
799
- const status = await (options.brokerControl?.status() ?? Promise.resolve({
800
- status: "stopped",
801
- brokerPid: null,
802
- instanceId: null,
803
- nextAction: "wow_broker_start"
804
- }));
805
- return {
806
- ...status,
807
- wowProcesses: processes.map(process => ({
808
- pid: process.pid,
809
- executable: process.executable,
810
- flavor: process.install.flavor,
811
- buildKey: process.install.build.buildKey,
812
- startTime: process.startTime ?? null
813
- }))
814
- };
815
- };
816
- const brokerControlTools = [
817
- ["wow_broker_status", "Broker status", "Read Broker state and list live Wow.exe PIDs without attaching or auto-starting.", READ_ONLY_ANNOTATIONS, brokerStatus],
818
- ["wow_broker_start", "Start Broker", "Start or connect to the per-user Broker.", READ_WRITE_ANNOTATIONS, () => options.brokerControl?.start() ?? Promise.resolve({ status: "unavailable" })],
819
- ["wow_broker_reconnect", "Reconnect Broker", "Reconnect MCP to the existing per-user Broker.", READ_WRITE_ANNOTATIONS, () => options.brokerControl?.reconnect() ?? Promise.resolve({ status: "unavailable" })],
820
- ["wow_broker_stop", "Stop Broker", "Release this MCP lease and request graceful Broker cleanup.", READ_WRITE_ANNOTATIONS, async () => { await options.brokerControl?.close(); return { status: "stopped" }; }]
821
- ];
822
- for (const [name, title, description, annotations, handler] of brokerControlTools) {
823
- server.registerTool(name, { title, description, inputSchema: fromJsonSchema(brokerStatusSchema), outputSchema: fridaResultSchema, annotations }, async () => textResult(await handler()));
824
- }
825
- server.registerResource("latest_lua_errors", LATEST_ERRORS_RESOURCE_URI, {
826
- title: "Latest WoW Lua errors",
827
- description: "The latest read-only snapshot from the JSONL error store.",
828
- mimeType: "application/json",
829
- cacheHint: { ttlMs: 0, cacheScope: "private" }
830
- }, async (uri) => {
831
- const result = await readLog(options.errorsFile);
832
- return {
833
- contents: [{
834
- uri: uri.href,
835
- mimeType: "application/json",
836
- text: JSON.stringify(result)
837
- }]
838
- };
839
- });
840
- return server;
841
- }
842
- function createUnavailableExecutor() {
843
- return {
844
- async execute() {
845
- throw new Error("Frida executor is not configured; connect MCP through the Broker gateway");
846
- },
847
- async close() { }
848
- };
849
- }
850
- function createUnavailableFocusExecutor() {
851
- return {
852
- async invoke() {
853
- throw new Error("Broker focus service is unavailable");
854
- }
855
- };
856
- }
857
- export async function resolveFridaTarget(command, discover, listProcesses, gameRoot, registry) {
858
- const operation = String(command.operation ?? command.op ?? command.command ?? "");
859
- const discoveryOperations = new Set(["devices", "processes", "applications", "spawn", "session_status"]);
860
- if (operation === "spawn" && command.pid !== undefined) {
861
- throw new Error("SPAWN_PID_MUST_BE_NULL: spawn cannot be bound to an existing PID");
862
- }
863
- if (operation === "spawn") {
864
- const buildKey = command.buildKey;
865
- return {
866
- pid: undefined,
867
- buildKey,
868
- flavor: command.flavor,
869
- adapter: buildKey ? registry.lookup(buildKey) : undefined
870
- };
871
- }
872
- if (command.pid === undefined && !discoveryOperations.has(operation)) {
873
- throw new Error("PID_REQUIRED: provide the live Wow.exe pid for this operation");
874
- }
875
- if (command.pid === undefined && command.buildKey === undefined && command.flavor === undefined) {
876
- return undefined;
877
- }
878
- const installs = discoverInstallsAcrossRoots(typeof gameRoot === "string" ? [gameRoot] : gameRoot, discover);
879
- const processes = await listProcesses(installs);
880
- const byPid = command.pid === undefined
881
- ? undefined
882
- : processes.find(item => item.pid === command.pid);
883
- if (command.pid !== undefined && !byPid) {
884
- throw new Error(`PID_NOT_WOW: PID ${command.pid} is not a discovered Wow.exe process`);
885
- }
886
- if (byPid && command.buildKey && byPid.install.build.buildKey !== command.buildKey) {
887
- throw new Error(`PID_BUILD_MISMATCH: PID ${byPid.pid} belongs to ${byPid.install.build.buildKey}, not ${command.buildKey}`);
888
- }
889
- if (byPid && command.flavor && byPid.install.flavor !== command.flavor) {
890
- throw new Error(`PID_BUILD_MISMATCH: PID ${byPid.pid} belongs to flavor ${byPid.install.flavor}, not ${command.flavor}`);
891
- }
892
- const process = byPid ?? processes.find(item => (command.buildKey === undefined || item.install.build.buildKey === command.buildKey) &&
893
- (command.flavor === undefined || item.install.flavor === command.flavor));
894
- const buildKey = process?.install.build.buildKey ?? command.buildKey;
895
- const flavor = process?.install.flavor ?? command.flavor;
896
- const adapter = buildKey ? registry.lookup(buildKey) : undefined;
897
- return {
898
- pid: process?.pid ?? command.pid,
899
- buildKey,
900
- flavor,
901
- executable: process?.executable,
902
- processStartTime: process?.startTime,
903
- adapter
904
- };
905
- }
906
- function requiredProfileRoot() {
907
- const value = process.env.WOWDUMP_PROFILE_DIR;
908
- if (!value)
909
- throw new Error("profileRoot is required; configure packaged build profiles");
910
- return resolve(value);
911
- }
912
- function normalizeGameRoots(options) {
913
- const roots = [...(options.gameRoots ?? []), ...(options.gameRoot ? [options.gameRoot] : [])];
914
- const unique = new Map();
915
- for (const root of roots) {
916
- const trimmed = root.trim();
917
- if (!trimmed)
918
- continue;
919
- const absolute = resolve(trimmed);
920
- if (!unique.has(absolute.toLowerCase()))
921
- unique.set(absolute.toLowerCase(), absolute);
922
- }
923
- return [...unique.values()];
924
- }