c64-debug-mcp 1.0.2 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/stdio.js CHANGED
@@ -365,10 +365,15 @@ function publicMessageFor(error) {
365
365
  case "program_file_missing":
366
366
  case "program_file_invalid":
367
367
  return error.message;
368
+ case "binary_not_found":
369
+ case "spawn_failed":
370
+ case "emulator_crashed_on_startup":
371
+ return error.message;
368
372
  case "port_allocation_failed":
369
373
  case "port_in_use":
370
- case "monitor_timeout":
371
374
  return "The server could not start a usable emulator session. Check the emulator configuration and try again.";
375
+ case "monitor_timeout":
376
+ return error.message;
372
377
  case "not_connected":
373
378
  case "connection_closed":
374
379
  case "socket_write_failed":
@@ -399,14 +404,21 @@ function publicMessageFor(error) {
399
404
  }
400
405
  }
401
406
  function publicDetailsFor(error) {
402
- switch (error.category) {
403
- case "validation":
404
- case "session_state":
405
- case "unsupported":
406
- case "io":
407
+ switch (error.code) {
408
+ case "binary_not_found":
409
+ case "spawn_failed":
410
+ case "emulator_crashed_on_startup":
407
411
  return error.details;
408
412
  default:
409
- return void 0;
413
+ switch (error.category) {
414
+ case "validation":
415
+ case "session_state":
416
+ case "unsupported":
417
+ case "io":
418
+ return error.details;
419
+ default:
420
+ return void 0;
421
+ }
410
422
  }
411
423
  }
412
424
  function normalizeToolError(error) {
@@ -1524,6 +1536,10 @@ var ViceSession = class {
1524
1536
  this.#syncMonitorRuntimeState();
1525
1537
  });
1526
1538
  }
1539
+ async getSessionState() {
1540
+ await this.#ensureReady();
1541
+ return this.snapshot();
1542
+ }
1527
1543
  snapshot() {
1528
1544
  return {
1529
1545
  transportState: this.#transportState,
@@ -1760,6 +1776,17 @@ var ViceSession = class {
1760
1776
  async continueExecution(waitUntilRunningStable = false) {
1761
1777
  return this.#withExecutionLock(async () => {
1762
1778
  await this.#ensureReady();
1779
+ if (this.#executionState === "running") {
1780
+ const runtime2 = this.#client.runtimeState();
1781
+ const debugState2 = this.#lastRegisters == null ? await this.#readDebugState() : this.#buildDebugState(this.#lastRegisters);
1782
+ return {
1783
+ executionState: "running",
1784
+ lastStopReason: this.#lastStopReason,
1785
+ programCounter: runtime2.programCounter ?? debugState2.programCounter,
1786
+ registers: debugState2.registers,
1787
+ warnings: []
1788
+ };
1789
+ }
1763
1790
  if (this.#executionState !== "stopped") {
1764
1791
  debuggerNotPausedError("execute resume", {
1765
1792
  executionState: this.#executionState,
@@ -1938,7 +1965,6 @@ var ViceSession = class {
1938
1965
  async programLoad(options) {
1939
1966
  const filePath = path.resolve(options.filePath);
1940
1967
  await this.#assertReadableProgramFile(filePath);
1941
- await this.#ensureRunning("program_load");
1942
1968
  this.#explicitPauseActive = false;
1943
1969
  const result = await this.autostartProgram(filePath, options.autoStart ?? true, options.fileIndex ?? 0);
1944
1970
  return {
@@ -2189,134 +2215,137 @@ var ViceSession = class {
2189
2215
  }
2190
2216
  }
2191
2217
  async writeText(text) {
2192
- await this.#ensureRunning("write_text");
2193
- const encoded = decodeWriteTextToPetscii(text);
2194
- if (encoded.length > MAX_WRITE_TEXT_BYTES) {
2195
- validationError("write_text exceeds the maximum allowed byte length for one request", {
2196
- length: encoded.length,
2197
- max: MAX_WRITE_TEXT_BYTES
2198
- });
2199
- }
2200
- this.#writeProcessLogLine(`[tx] write_text length=${encoded.length} text=${JSON.stringify(text)}`);
2201
- await this.#client.sendKeys(Buffer.from(encoded).toString("binary"));
2202
- await this.#settleInputState("write_text", "running");
2203
- return {
2204
- sent: true,
2205
- length: encoded.length
2206
- };
2218
+ return await this.#withAutoResumeForInput("write_text", async () => {
2219
+ const encoded = decodeWriteTextToPetscii(text);
2220
+ if (encoded.length > MAX_WRITE_TEXT_BYTES) {
2221
+ validationError("write_text exceeds the maximum allowed byte length for one request", {
2222
+ length: encoded.length,
2223
+ max: MAX_WRITE_TEXT_BYTES
2224
+ });
2225
+ }
2226
+ this.#writeProcessLogLine(`[tx] write_text length=${encoded.length} text=${JSON.stringify(text)}`);
2227
+ await this.#client.sendKeys(Buffer.from(encoded).toString("binary"));
2228
+ await this.#settleInputState("write_text", "running");
2229
+ return {
2230
+ sent: true,
2231
+ length: encoded.length
2232
+ };
2233
+ });
2207
2234
  }
2208
2235
  async keyboardInput(action, keys, durationMs) {
2209
- await this.#ensureRunning("keyboard_input");
2210
- if (!Array.isArray(keys) || keys.length === 0 || keys.length > 4) {
2211
- validationError("keyboard_input requires between 1 and 4 keys", { keys });
2212
- }
2213
- const resolvedKeys = keys.map((key) => resolveKeyboardInputKey(key));
2214
- const normalizedKeys = resolvedKeys.map((key) => key.canonical);
2215
- this.#writeProcessLogLine(
2216
- `[tx] keyboard_input action=${action} keys=${normalizedKeys.join(",")}${durationMs == null ? "" : ` durationMs=${durationMs}`}`
2217
- );
2218
- switch (action) {
2219
- case "tap": {
2220
- const duration = clampTapDuration(durationMs);
2221
- const bytes = Uint8Array.from(resolvedKeys.flatMap((key) => Array.from(key.bytes)));
2222
- await this.#client.sendKeys(Buffer.from(bytes).toString("binary"));
2223
- await this.#settleInputState("keyboard_input", "running");
2224
- await sleep(duration);
2225
- return {
2226
- action,
2227
- keys: normalizedKeys,
2228
- applied: true,
2229
- held: false,
2230
- mode: "buffered_text"
2231
- };
2236
+ return await this.#withAutoResumeForInput("keyboard_input", async () => {
2237
+ if (!Array.isArray(keys) || keys.length === 0 || keys.length > 4) {
2238
+ validationError("keyboard_input requires between 1 and 4 keys", { keys });
2232
2239
  }
2233
- case "press": {
2234
- const singleByteKeys = resolvedKeys.map((key) => {
2235
- if (key.bytes.length !== 1) {
2236
- unsupportedError("keyboard_input press/release only supports keys that map to a single PETSCII byte.", {
2237
- key: key.canonical
2238
- });
2239
- }
2240
- return key.bytes[0];
2241
- });
2242
- for (let index = 0; index < normalizedKeys.length; index += 1) {
2243
- const heldKey = normalizedKeys[index];
2244
- const byte = singleByteKeys[index];
2245
- if (!this.#heldKeyboardIntervals.has(heldKey)) {
2246
- await this.#client.sendKeys(Buffer.from([byte]).toString("binary"));
2247
- await this.#settleInputState("keyboard_input", "running");
2248
- const interval = setInterval(() => {
2249
- void this.#client.sendKeys(Buffer.from([byte]).toString("binary")).then(() => this.#settleInputState("keyboard_input", "running")).catch(() => void 0);
2250
- }, DEFAULT_KEYBOARD_REPEAT_MS);
2251
- this.#heldKeyboardIntervals.set(heldKey, interval);
2252
- }
2240
+ const resolvedKeys = keys.map((key) => resolveKeyboardInputKey(key));
2241
+ const normalizedKeys = resolvedKeys.map((key) => key.canonical);
2242
+ this.#writeProcessLogLine(
2243
+ `[tx] keyboard_input action=${action} keys=${normalizedKeys.join(",")}${durationMs == null ? "" : ` durationMs=${durationMs}`}`
2244
+ );
2245
+ switch (action) {
2246
+ case "tap": {
2247
+ const duration = clampTapDuration(durationMs);
2248
+ const bytes = Uint8Array.from(resolvedKeys.flatMap((key) => Array.from(key.bytes)));
2249
+ await this.#client.sendKeys(Buffer.from(bytes).toString("binary"));
2250
+ await this.#settleInputState("keyboard_input", "running");
2251
+ await sleep(duration);
2252
+ return {
2253
+ action,
2254
+ keys: normalizedKeys,
2255
+ applied: true,
2256
+ held: false,
2257
+ mode: "buffered_text"
2258
+ };
2253
2259
  }
2254
- return {
2255
- action,
2256
- keys: normalizedKeys,
2257
- applied: true,
2258
- held: true,
2259
- mode: "buffered_text_repeat"
2260
- };
2261
- }
2262
- case "release": {
2263
- for (const key of resolvedKeys) {
2264
- if (key.bytes.length !== 1) {
2265
- unsupportedError("keyboard_input press/release only supports keys that map to a single PETSCII byte.", {
2266
- key: key.canonical
2267
- });
2260
+ case "press": {
2261
+ const singleByteKeys = resolvedKeys.map((key) => {
2262
+ if (key.bytes.length !== 1) {
2263
+ unsupportedError("keyboard_input press/release only supports keys that map to a single PETSCII byte.", {
2264
+ key: key.canonical
2265
+ });
2266
+ }
2267
+ return key.bytes[0];
2268
+ });
2269
+ for (let index = 0; index < normalizedKeys.length; index += 1) {
2270
+ const heldKey = normalizedKeys[index];
2271
+ const byte = singleByteKeys[index];
2272
+ if (!this.#heldKeyboardIntervals.has(heldKey)) {
2273
+ await this.#client.sendKeys(Buffer.from([byte]).toString("binary"));
2274
+ await this.#settleInputState("keyboard_input", "running");
2275
+ const interval = setInterval(() => {
2276
+ void this.#client.sendKeys(Buffer.from([byte]).toString("binary")).then(() => this.#settleInputState("keyboard_input", "running")).catch(() => void 0);
2277
+ }, DEFAULT_KEYBOARD_REPEAT_MS);
2278
+ this.#heldKeyboardIntervals.set(heldKey, interval);
2279
+ }
2268
2280
  }
2281
+ return {
2282
+ action,
2283
+ keys: normalizedKeys,
2284
+ applied: true,
2285
+ held: true,
2286
+ mode: "buffered_text_repeat"
2287
+ };
2269
2288
  }
2270
- for (const heldKey of normalizedKeys) {
2271
- const interval = this.#heldKeyboardIntervals.get(heldKey);
2272
- if (interval) {
2273
- clearInterval(interval);
2274
- this.#heldKeyboardIntervals.delete(heldKey);
2289
+ case "release": {
2290
+ for (const key of resolvedKeys) {
2291
+ if (key.bytes.length !== 1) {
2292
+ unsupportedError("keyboard_input press/release only supports keys that map to a single PETSCII byte.", {
2293
+ key: key.canonical
2294
+ });
2295
+ }
2275
2296
  }
2297
+ for (const heldKey of normalizedKeys) {
2298
+ const interval = this.#heldKeyboardIntervals.get(heldKey);
2299
+ if (interval) {
2300
+ clearInterval(interval);
2301
+ this.#heldKeyboardIntervals.delete(heldKey);
2302
+ }
2303
+ }
2304
+ return {
2305
+ action,
2306
+ keys: normalizedKeys,
2307
+ applied: true,
2308
+ held: false,
2309
+ mode: "buffered_text_repeat"
2310
+ };
2276
2311
  }
2277
- return {
2278
- action,
2279
- keys: normalizedKeys,
2280
- applied: true,
2281
- held: false,
2282
- mode: "buffered_text_repeat"
2283
- };
2284
2312
  }
2285
- }
2313
+ });
2286
2314
  }
2287
2315
  async joystickInput(port, action, control, durationMs) {
2288
- await this.#ensureRunning("joystick_input");
2289
- const previousExecutionState = this.#executionState;
2290
- const bit = JOYSTICK_CONTROL_BITS[control];
2291
- if (bit == null) {
2292
- validationError("Unsupported joystick control", { control });
2293
- }
2294
- this.#writeProcessLogLine(
2295
- `[tx] joystick_input port=${port} action=${action} control=${control}${durationMs == null ? "" : ` durationMs=${durationMs}`}`
2296
- );
2297
- switch (action) {
2298
- case "tap": {
2299
- const duration = clampTapDuration(durationMs);
2300
- await this.#applyJoystickMask(port, this.#getJoystickMask(port) & ~bit);
2301
- await sleep(duration);
2302
- await this.#applyJoystickMask(port, this.#getJoystickMask(port) | bit);
2303
- break;
2316
+ return await this.#withAutoResumeForInput("joystick_input", async () => {
2317
+ const previousExecutionState = this.#executionState;
2318
+ const bit = JOYSTICK_CONTROL_BITS[control];
2319
+ if (bit == null) {
2320
+ validationError("Unsupported joystick control", { control });
2304
2321
  }
2305
- case "press":
2306
- await this.#applyJoystickMask(port, this.#getJoystickMask(port) & ~bit);
2307
- break;
2308
- case "release":
2309
- await this.#applyJoystickMask(port, this.#getJoystickMask(port) | bit);
2310
- break;
2311
- }
2312
- await this.#settleInputState("joystick_input", previousExecutionState);
2313
- return {
2314
- port,
2315
- action,
2316
- control,
2317
- applied: true,
2318
- state: this.#describeJoystickState(port)
2319
- };
2322
+ this.#writeProcessLogLine(
2323
+ `[tx] joystick_input port=${port} action=${action} control=${control}${durationMs == null ? "" : ` durationMs=${durationMs}`}`
2324
+ );
2325
+ switch (action) {
2326
+ case "tap": {
2327
+ const duration = clampTapDuration(durationMs);
2328
+ await this.#applyJoystickMask(port, this.#getJoystickMask(port) & ~bit);
2329
+ await sleep(duration);
2330
+ await this.#applyJoystickMask(port, this.#getJoystickMask(port) | bit);
2331
+ break;
2332
+ }
2333
+ case "press":
2334
+ await this.#applyJoystickMask(port, this.#getJoystickMask(port) & ~bit);
2335
+ break;
2336
+ case "release":
2337
+ await this.#applyJoystickMask(port, this.#getJoystickMask(port) | bit);
2338
+ break;
2339
+ }
2340
+ await this.#settleInputState("joystick_input", previousExecutionState);
2341
+ return {
2342
+ port,
2343
+ action,
2344
+ control,
2345
+ applied: true,
2346
+ state: this.#describeJoystickState(port)
2347
+ };
2348
+ });
2320
2349
  }
2321
2350
  async waitForState(targetState, timeoutMs = 5e3, stableMs = targetState === "running" ? INPUT_RUNNING_STABLE_MS : 0) {
2322
2351
  await this.#ensureReady();
@@ -2379,6 +2408,27 @@ var ViceSession = class {
2379
2408
  });
2380
2409
  }
2381
2410
  }
2411
+ async #withAutoResumeForInput(commandName, operation) {
2412
+ await this.#ensureReady();
2413
+ this.#syncMonitorRuntimeState();
2414
+ const wasRunning = this.#executionState === "running";
2415
+ const wasPaused = this.#explicitPauseActive;
2416
+ if (!wasRunning) {
2417
+ this.#writeProcessLogLine(`[${commandName}] auto-resuming for input operation`);
2418
+ await this.#client.continueExecution();
2419
+ await this.waitForState("running", 5e3, INPUT_RUNNING_STABLE_MS);
2420
+ }
2421
+ try {
2422
+ return await operation();
2423
+ } finally {
2424
+ if (!wasRunning && wasPaused) {
2425
+ this.#writeProcessLogLine(`[${commandName}] restoring paused state after input`);
2426
+ await this.#client.ping();
2427
+ await this.waitForState("stopped", 5e3, 0);
2428
+ this.#explicitPauseActive = true;
2429
+ }
2430
+ }
2431
+ }
2382
2432
  async #ensureHealthyConnection() {
2383
2433
  if (this.#recoveryPromise) {
2384
2434
  await this.#recoveryPromise;
@@ -2427,6 +2477,16 @@ var ViceSession = class {
2427
2477
  const port = await this.#portAllocator.allocate();
2428
2478
  await this.#portAllocator.ensureFree(port, host);
2429
2479
  const binary = config.binaryPath ?? DEFAULT_C64_BINARY;
2480
+ const binaryCheck = await checkBinaryExists(binary);
2481
+ if (!binaryCheck.exists) {
2482
+ throw new ViceMcpError(
2483
+ "binary_not_found",
2484
+ `VICE emulator binary '${binary}' not found. Please install VICE or configure the correct path using the 'binaryPath' setting.`,
2485
+ "process_launch",
2486
+ false,
2487
+ { binary, searchedPath: process.env.PATH }
2488
+ );
2489
+ }
2430
2490
  const args = ["-autostartprgmode", "1", "-binarymonitor", "-binarymonitoraddress", `${host}:${port}`];
2431
2491
  if (config.arguments) {
2432
2492
  args.push(...splitCommandLine(config.arguments));
@@ -2444,11 +2504,15 @@ var ViceSession = class {
2444
2504
  this.#lastRuntimeEventType = "unknown";
2445
2505
  this.#lastRuntimeProgramCounter = null;
2446
2506
  const env = await buildViceLaunchEnv();
2507
+ let spawnError = void 0;
2447
2508
  const child = spawn(binary, args, {
2448
2509
  cwd: config.workingDirectory ? path.resolve(config.workingDirectory) : void 0,
2449
2510
  env,
2450
2511
  stdio: ["ignore", "pipe", "pipe"]
2451
2512
  });
2513
+ child.once("error", (err) => {
2514
+ spawnError = err;
2515
+ });
2452
2516
  this.#process = child;
2453
2517
  this.#attachProcessLogging(child, binary, args);
2454
2518
  this.#bindProcessLifecycle(child);
@@ -2456,6 +2520,16 @@ var ViceSession = class {
2456
2520
  this.#transportState = "waiting_for_monitor";
2457
2521
  try {
2458
2522
  await waitForMonitor(host, port, 5e3);
2523
+ if (spawnError !== void 0) {
2524
+ const err = spawnError;
2525
+ throw new ViceMcpError(
2526
+ "spawn_failed",
2527
+ `Failed to start VICE emulator '${binary}': ${err.message}`,
2528
+ "process_launch",
2529
+ false,
2530
+ { binary, error: err.message, resolvedPath: binaryCheck.path }
2531
+ );
2532
+ }
2459
2533
  await this.#client.connect(host, port);
2460
2534
  this.#transportState = "connected";
2461
2535
  this.#connectedSince = nowIso();
@@ -2468,6 +2542,19 @@ var ViceSession = class {
2468
2542
  } catch (error) {
2469
2543
  this.#processState = "crashed";
2470
2544
  this.#transportState = "faulted";
2545
+ if (error instanceof ViceMcpError && error.code === "monitor_timeout" && spawnError !== void 0) {
2546
+ const err = spawnError;
2547
+ const enhancedError = new ViceMcpError(
2548
+ "emulator_crashed_on_startup",
2549
+ `VICE emulator '${binary}' crashed during startup: ${err.message}`,
2550
+ "process_launch",
2551
+ false,
2552
+ { binary, error: err.message, resolvedPath: binaryCheck.path }
2553
+ );
2554
+ this.#warnings = [...this.#warnings.filter((warning) => warning.code !== "launch_failed"), makeWarning(enhancedError.message, "launch_failed")];
2555
+ await this.#stopManagedProcess(true);
2556
+ throw enhancedError;
2557
+ }
2471
2558
  this.#warnings = [...this.#warnings.filter((warning) => warning.code !== "launch_failed"), makeWarning(String(error.message ?? error), "launch_failed")];
2472
2559
  await this.#stopManagedProcess(true);
2473
2560
  throw error;
@@ -3208,6 +3295,27 @@ function splitCommandLine(input) {
3208
3295
  }
3209
3296
  return result;
3210
3297
  }
3298
+ async function checkBinaryExists(binaryPath) {
3299
+ if (path.isAbsolute(binaryPath)) {
3300
+ try {
3301
+ await fs.access(binaryPath, fs.constants.X_OK);
3302
+ return { exists: true, path: binaryPath };
3303
+ } catch {
3304
+ return { exists: false };
3305
+ }
3306
+ }
3307
+ const pathEnv = process.env.PATH || "";
3308
+ const pathDirs = pathEnv.split(path.delimiter);
3309
+ for (const dir of pathDirs) {
3310
+ const fullPath = path.join(dir, binaryPath);
3311
+ try {
3312
+ await fs.access(fullPath, fs.constants.X_OK);
3313
+ return { exists: true, path: fullPath };
3314
+ } catch {
3315
+ }
3316
+ }
3317
+ return { exists: false };
3318
+ }
3211
3319
  async function waitForMonitor(host, port, timeoutMs) {
3212
3320
  const deadline = Date.now() + timeoutMs;
3213
3321
  while (Date.now() < deadline) {
@@ -3216,7 +3324,7 @@ async function waitForMonitor(host, port, timeoutMs) {
3216
3324
  }
3217
3325
  await sleep(100);
3218
3326
  }
3219
- throw new ViceMcpError("monitor_timeout", `Debugger monitor did not open on ${host}:${port}`, "timeout", true, {
3327
+ throw new ViceMcpError("monitor_timeout", `Debugger monitor did not open on ${host}:${port}. The emulator may have failed to start or crashed during startup.`, "timeout", true, {
3220
3328
  host,
3221
3329
  port
3222
3330
  });
@@ -3281,7 +3389,7 @@ var getSessionStateTool = createViceTool({
3281
3389
  description: "Returns emulator session state including transport/process status, auto-resume state, and the most recent hit checkpoint.",
3282
3390
  inputSchema: noInputSchema,
3283
3391
  dataSchema: sessionStateResultSchema,
3284
- execute: async () => c64Session.snapshot()
3392
+ execute: async () => await c64Session.getSessionState()
3285
3393
  });
3286
3394
  var getRegistersTool = createViceTool({
3287
3395
  id: "get_registers",
@@ -3347,7 +3455,7 @@ var writeMemoryTool = createViceTool({
3347
3455
  });
3348
3456
  var executeTool = createViceTool({
3349
3457
  id: "execute",
3350
- description: "Controls execution with pause, resume, step, step_over, step_out, or reset. Resume can optionally wait until running becomes stable.",
3458
+ description: "Controls execution with pause, resume, step, step_over, step_out, or reset. Pause and resume are idempotent (safe to call multiple times).",
3351
3459
  inputSchema: z3.object({
3352
3460
  action: z3.enum(["pause", "resume", "step", "step_over", "step_out", "reset"]),
3353
3461
  count: z3.number().int().positive().default(1).describe("Instruction count for step and step_over actions"),
@@ -3469,7 +3577,7 @@ var getDisplayTextTool = createViceTool({
3469
3577
  });
3470
3578
  var writeTextTool = createViceTool({
3471
3579
  id: "write_text",
3472
- description: 'Types text into the C64. Requires emulator to be running - call execute(action="resume") first if stopped. Supports escaped characters and PETSCII brace tokens like {RETURN}, {CLR}, {HOME}, {PI}, and color names. Limit 64 bytes per request.',
3580
+ description: "Types text into the C64. Automatically resumes if stopped and restores pause state after. Supports escaped characters and PETSCII brace tokens like {RETURN}, {CLR}, {HOME}, {PI}, and color names. Limit 64 bytes per request.",
3473
3581
  inputSchema: z3.object({
3474
3582
  text: z3.string()
3475
3583
  }),
@@ -3481,7 +3589,7 @@ var writeTextTool = createViceTool({
3481
3589
  });
3482
3590
  var keyboardInputTool = createViceTool({
3483
3591
  id: "keyboard_input",
3484
- description: 'Sends one to four keys or PETSCII tokens to the C64. Requires emulator to be running - call execute(action="resume") first if stopped. Use for key presses, releases, and taps.',
3592
+ description: "Sends one to four keys or PETSCII tokens to the C64. Automatically resumes if stopped and restores pause state after. Use for key presses, releases, and taps.",
3485
3593
  inputSchema: z3.object({
3486
3594
  action: inputActionSchema.describe("Use tap for a single key event or press/release for repeated buffered input"),
3487
3595
  keys: z3.array(z3.string().min(1)).min(1).max(4).describe("One to four literal keys or PETSCII token names such as RETURN, CLR, HOME, PI, LEFT, RED, or F1"),
@@ -3492,7 +3600,7 @@ var keyboardInputTool = createViceTool({
3492
3600
  });
3493
3601
  var joystickInputTool = createViceTool({
3494
3602
  id: "joystick_input",
3495
- description: 'Sends joystick input to C64 joystick port 1 or 2. Requires emulator to be running - call execute(action="resume") first if stopped.',
3603
+ description: "Sends joystick input to C64 joystick port 1 or 2. Automatically resumes if stopped and restores pause state after.",
3496
3604
  inputSchema: z3.object({
3497
3605
  port: joystickPortSchema.describe("Joystick port number"),
3498
3606
  action: inputActionSchema.describe("Joystick action to apply"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c64-debug-mcp",
3
- "version": "1.0.2",
3
+ "version": "1.0.6",
4
4
  "description": "Model Context Protocol server for C64 debugging via VICE emulator",
5
5
  "type": "module",
6
6
  "keywords": [