wowdump 0.2.1 → 0.3.2
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/LICENSE +1 -1
- package/README.md +17 -111
- package/dist/adapters/reader.js +33 -0
- package/dist/analysis/disassemble.js +77 -0
- package/dist/{frida-runtime.js → analysis/frida-runtime.js} +3 -25
- package/dist/analysis/runtime-script.js +36 -0
- package/dist/cli.js +563 -0
- package/dist/core/profile-engine.js +238 -0
- package/dist/frida-worker.js +99 -0
- package/dist/reader/broker.js +475 -0
- package/dist/reader/client.js +1 -0
- package/dist/reader/launcher.js +219 -0
- package/dist/reader/main.js +100 -0
- package/dist/reader/protocol.js +1 -0
- package/dist/reader/windows.js +242 -0
- package/dist/reader-main.js +2 -0
- package/dist/toolchain.js +123 -0
- package/package.json +19 -37
- package/skills/wowdump/SKILL.md +22 -0
- package/skills/wowdump/references/commands.md +63 -0
- package/skills/wowdump/references/disassemble.md +18 -0
- package/skills/wowdump/references/dynamic.md +54 -0
- package/skills/wowdump/references/evidence-workflow.md +41 -0
- package/skills/wowdump/references/profiles.md +34 -0
- package/skills/wowdump/references/request-schema.md +28 -0
- package/skills/wowdump/references/workflow.md +44 -0
- package/skills/wowdump/scripts/dynamic-session.js +133 -0
- package/dist/agent.js +0 -1335
- package/dist/analysis-path.js +0 -38
- package/dist/analysis-process-log.js +0 -146
- package/dist/broker-client.js +0 -411
- package/dist/broker-codec.js +0 -148
- package/dist/broker-core.js +0 -1045
- package/dist/broker-gateway.js +0 -447
- package/dist/broker-ledger.js +0 -196
- package/dist/broker-main.js +0 -291
- package/dist/broker-protocol.js +0 -119
- package/dist/broker-runtime.js +0 -1283
- package/dist/broker-server.js +0 -466
- package/dist/build-bundle-loader.js +0 -183
- package/dist/build-bundle.js +0 -11
- package/dist/discovery.js +0 -59
- package/dist/dry-run.js +0 -38
- package/dist/error-log.js +0 -71
- package/dist/focus-errors.js +0 -63
- package/dist/focus-service.js +0 -1855
- package/dist/focused-session.js +0 -1357
- package/dist/mcp-main.js +0 -51
- package/dist/mcp.js +0 -924
- package/dist/observability.js +0 -41
- package/dist/process-log-lock.js +0 -195
- package/dist/processes.js +0 -47
- package/dist/runtime-config.js +0 -399
- package/dist/session.js +0 -145
- package/dist/storage.js +0 -12
- package/dist/wow-analysis.js +0 -1430
- package/resources/builds/retail/12.0.7.68974/build-profile.json +0 -290
- package/resources/builds/retail/12.0.7.68974/data-sources.json +0 -1633
- package/resources/builds/retail/12.0.7.68974/lua-targets.jsonl +0 -5130
- package/resources/builds/retail/12.0.7.68974/manifest.json +0 -63
- package/resources/builds/retail/12.0.7.68974/signatures.json +0 -260
- /package/dist/{adapters.js → core/build-adapters.js} +0 -0
- /package/dist/{types.js → core/types.js} +0 -0
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
|
-
}
|