c64bridge 0.9.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/AGENTS.md +19 -3
  2. package/README.md +190 -148
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/c64Client.js +127 -5
  5. package/dist/config.js +12 -8
  6. package/dist/device.js +75 -45
  7. package/dist/mcp-server.js +11 -3
  8. package/dist/platform.js +13 -2
  9. package/dist/tools/audio.js +1 -1
  10. package/dist/tools/developer.js +6 -6
  11. package/dist/tools/graphics.js +4 -4
  12. package/dist/tools/input.js +129 -12
  13. package/dist/tools/machineControl.js +85 -6
  14. package/dist/tools/memory.js +1 -1
  15. package/dist/tools/meta/index.js +1 -1
  16. package/dist/tools/programRunners.js +5 -5
  17. package/dist/tools/rag.js +1 -1
  18. package/dist/tools/registry/batch.js +1 -1
  19. package/dist/tools/registry/config.js +5 -5
  20. package/dist/tools/registry/disk.js +2 -2
  21. package/dist/tools/registry/drive.js +2 -2
  22. package/dist/tools/registry/graphics.js +2 -2
  23. package/dist/tools/registry/memory.js +2 -2
  24. package/dist/tools/registry/platform.js +7 -5
  25. package/dist/tools/registry/program.js +5 -5
  26. package/dist/tools/registry/rag.js +1 -1
  27. package/dist/tools/registry/sound.js +4 -4
  28. package/dist/tools/registry/system.js +13 -3
  29. package/dist/tools/storage.js +1 -1
  30. package/doc/c64u/c64-openapi.yaml +1130 -774
  31. package/doc/u2/u2-openapi.yaml +1192 -0
  32. package/generated/c64/index.js +142 -9
  33. package/generated/openapi/c64u/index.js +1117 -0
  34. package/generated/openapi/u2/index.js +929 -0
  35. package/mcp/schemas/c64_graphics.schema.json +3 -3
  36. package/mcp/schemas/c64_input.schema.json +247 -6
  37. package/mcp/schemas/c64_memory.schema.json +3 -3
  38. package/mcp/schemas/c64_program.schema.json +3 -3
  39. package/mcp/schemas/c64_select_backend.schema.json +2 -0
  40. package/mcp/schemas/c64_system.schema.json +41 -1
  41. package/mcp/server.json +1 -1
  42. package/mcp/tools.json +382 -41
  43. package/mcp.json +14 -2
  44. package/package.json +4 -2
  45. package/scripts/mockC64Server.mjs +40 -0
  46. package/scripts/prepare-release.mjs +13 -2
  47. package/scripts/update-readme.ts +18 -30
  48. package/server.json +3 -3
@@ -1,7 +1,16 @@
1
1
  import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema, } from "./types.js";
2
2
  import { arraySchema, literalSchema, numberSchema, objectSchema, optionalSchema, stringSchema, } from "./schema.js";
3
- import { textResult } from "./responses.js";
3
+ import { jsonResult, textResult } from "./responses.js";
4
4
  import { ToolValidationError, toolErrorResult, unknownErrorResult, } from "./errors.js";
5
+ function isMissingNativeInputEndpoint(error) {
6
+ return typeof error === "object"
7
+ && error !== null
8
+ && "response" in error
9
+ && error.response?.status === 404;
10
+ }
11
+ function nativeInputUnavailableResult(operation) {
12
+ return textResult(`Native physical input is unavailable on this firmware, so ${operation} was not sent. Use c64://platform/status before choosing native input; use key or write_text for ordinary C64 input.`, { success: false, code: "native_input_unavailable", fallback: "key_or_write_text" });
13
+ }
5
14
  // ---------------------------------------------------------------------------
6
15
  // PETSCII token expansion table
7
16
  // Maps {TOKEN} placeholders to PETSCII/control-byte values that are queued via
@@ -96,6 +105,14 @@ const JOYSTICK_BIT = {
96
105
  right: 3,
97
106
  fire: 4,
98
107
  };
108
+ const KEYBOARD_INPUTS = [
109
+ "inst_del", "return", "cursor_left_right", "f7", "f1", "f3", "f5", "cursor_up_down",
110
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
111
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
112
+ "left_shift", "right_shift", "plus", "minus", "period", "colon", "at", "comma", "pound", "star", "semicolon", "clr_home", "equals", "arrow_up", "slash", "arrow_left", "ctrl", "space", "commodore", "run_stop", "restore",
113
+ ];
114
+ const JOYSTICK_INPUTS = ["up", "down", "left", "right", "fire", "fire2", "fire3"];
115
+ const INPUT_TRANSITIONS = ["press", "release", "tap"];
99
116
  function joystickByte(controls) {
100
117
  let mask = 0xff;
101
118
  for (const ctrl of controls) {
@@ -157,7 +174,7 @@ export const keyArgsSchema = objectSchema({
157
174
  additionalProperties: false,
158
175
  });
159
176
  export const joystickArgsSchema = objectSchema({
160
- description: "Simulate joystick input by writing directly to CIA1 Port A/B registers.",
177
+ description: "Simulate joystick input. On C64U/U64 this uses machine:input (a C64U firmware version that provides it, or U64 3.15+); VICE writes CIA1 registers.",
161
178
  properties: {
162
179
  op: literalSchema("joystick"),
163
180
  port: numberSchema({
@@ -168,7 +185,7 @@ export const joystickArgsSchema = objectSchema({
168
185
  }),
169
186
  controls: arraySchema(stringSchema({
170
187
  description: "Control to activate.",
171
- enum: ["up", "down", "left", "right", "fire"],
188
+ enum: JOYSTICK_INPUTS,
172
189
  }), {
173
190
  description: "List of controls to activate simultaneously.",
174
191
  minItems: 0,
@@ -188,6 +205,32 @@ export const joystickArgsSchema = objectSchema({
188
205
  required: ["op", "port", "controls", "action"],
189
206
  additionalProperties: false,
190
207
  });
208
+ export const keyboardArgsSchema = objectSchema({
209
+ description: "Send physical C64 keyboard matrix events through machine:input (a C64U firmware version that provides it, or U64 3.15+).",
210
+ properties: {
211
+ op: literalSchema("keyboard"),
212
+ inputs: arraySchema(stringSchema({ description: "Physical keyboard input.", enum: KEYBOARD_INPUTS }), {
213
+ description: "One or more keys to transition together (for example left_shift + a).",
214
+ minItems: 1,
215
+ maxItems: 8,
216
+ }),
217
+ transition: stringSchema({ description: "press holds, release frees, tap presses and releases.", enum: INPUT_TRANSITIONS }),
218
+ },
219
+ required: ["op", "inputs", "transition"],
220
+ additionalProperties: false,
221
+ });
222
+ const releaseAllArgsSchema = objectSchema({
223
+ description: "Release every key and joystick control injected through machine:input (a C64U firmware version that provides it, or U64 3.15+).",
224
+ properties: { op: literalSchema("release_all") },
225
+ required: ["op"],
226
+ additionalProperties: false,
227
+ });
228
+ const inputStateArgsSchema = objectSchema({
229
+ description: "Read the keys and joystick controls held through machine:input (a C64U firmware version that provides it, or U64 3.15+).",
230
+ properties: { op: literalSchema("state") },
231
+ required: ["op"],
232
+ additionalProperties: false,
233
+ });
191
234
  // ---------------------------------------------------------------------------
192
235
  // Handlers
193
236
  // ---------------------------------------------------------------------------
@@ -250,10 +293,26 @@ const inputOperationHandlers = {
250
293
  try {
251
294
  const parsed = joystickArgsSchema.parse(args);
252
295
  const port = parsed.port;
253
- const addr = JOYSTICK_PORT_ADDRESS[port];
254
296
  const durationMs = parsed.durationMs ?? 80;
255
297
  const pressedByte = joystickByte(parsed.controls);
256
298
  const releasedByte = 0xff;
299
+ const platform = await ctx.client.getActiveBackendType();
300
+ if (platform === "c64u") {
301
+ if (parsed.controls.length === 0 && parsed.action === "release") {
302
+ const state = await ctx.client.sendInputEvents({ events: [{ kind: "release_all" }] });
303
+ return textResult("Released all REST-injected keyboard and joystick inputs.", { success: true, action: "release_all", state });
304
+ }
305
+ const state = await ctx.client.sendInputEvents({
306
+ events: [{ kind: "joystick", port, inputs: [...parsed.controls], transition: parsed.action }],
307
+ });
308
+ return textResult(`Joystick port ${port} ${parsed.action}: ${parsed.controls.join(", ")}.`, {
309
+ success: true, port, action: parsed.action, controls: parsed.controls, state,
310
+ });
311
+ }
312
+ const addr = JOYSTICK_PORT_ADDRESS[port];
313
+ if (parsed.controls.some((control) => !(control in JOYSTICK_BIT))) {
314
+ throw new ToolValidationError("VICE joystick supports up, down, left, right, and fire only.", { path: "$.controls" });
315
+ }
257
316
  if (parsed.action === "release") {
258
317
  await ctx.client.viceMemSet(addr, Uint8Array.of(releasedByte));
259
318
  ctx.logger.info("Released joystick", { port, address: `$${addr.toString(16).toUpperCase()}` });
@@ -274,6 +333,53 @@ const inputOperationHandlers = {
274
333
  catch (error) {
275
334
  if (error instanceof ToolValidationError)
276
335
  return toolErrorResult(error);
336
+ if (isMissingNativeInputEndpoint(error))
337
+ return nativeInputUnavailableResult("the joystick event");
338
+ return unknownErrorResult(error);
339
+ }
340
+ },
341
+ keyboard: async (args, ctx) => {
342
+ try {
343
+ const parsed = keyboardArgsSchema.parse(args);
344
+ const state = await ctx.client.sendInputEvents({
345
+ events: [{ kind: "keyboard", inputs: [...parsed.inputs], transition: parsed.transition }],
346
+ });
347
+ return textResult(`Keyboard ${parsed.transition}: ${parsed.inputs.join(", ")}.`, {
348
+ success: true, inputs: parsed.inputs, transition: parsed.transition, state,
349
+ });
350
+ }
351
+ catch (error) {
352
+ if (error instanceof ToolValidationError)
353
+ return toolErrorResult(error);
354
+ if (isMissingNativeInputEndpoint(error))
355
+ return nativeInputUnavailableResult("the keyboard event");
356
+ return unknownErrorResult(error);
357
+ }
358
+ },
359
+ release_all: async (args, ctx) => {
360
+ try {
361
+ releaseAllArgsSchema.parse(args);
362
+ const state = await ctx.client.sendInputEvents({ events: [{ kind: "release_all" }] });
363
+ return textResult("Released all REST-injected keyboard and joystick inputs.", { success: true, state });
364
+ }
365
+ catch (error) {
366
+ if (error instanceof ToolValidationError)
367
+ return toolErrorResult(error);
368
+ if (isMissingNativeInputEndpoint(error))
369
+ return nativeInputUnavailableResult("the release request");
370
+ return unknownErrorResult(error);
371
+ }
372
+ },
373
+ state: async (args, ctx) => {
374
+ try {
375
+ inputStateArgsSchema.parse(args);
376
+ return jsonResult(await ctx.client.getInputState());
377
+ }
378
+ catch (error) {
379
+ if (error instanceof ToolValidationError)
380
+ return toolErrorResult(error);
381
+ if (isMissingNativeInputEndpoint(error))
382
+ return nativeInputUnavailableResult("the input-state query");
277
383
  return unknownErrorResult(error);
278
384
  }
279
385
  },
@@ -283,30 +389,36 @@ const inputOperationHandlers = {
283
389
  // ---------------------------------------------------------------------------
284
390
  export const inputModule = defineToolModule({
285
391
  domain: "input",
286
- summary: "Cross-platform keyboard input plus VICE-only joystick simulation.",
287
- supportedPlatforms: ["c64u", "vice"],
392
+ summary: "Keyboard input plus native Ultimate REST keyboard and joystick control.",
393
+ supportedPlatforms: ["c64u", "u2", "vice"],
288
394
  resources: ["c64://guide/bootstrap", "c64://memory/map", "c64://io/cia/spec"],
289
395
  prompts: [],
290
396
  defaultTags: ["input"],
291
397
  workflowHints: [
292
- "Use write_text with {RETURN} tokens to automate BASIC entry; prefer key for individual control keys.",
398
+ "Use write_text with {RETURN} tokens to automate BASIC entry; use key for one PETSCII/KERNAL queue key. Neither is for navigating Ultimate firmware menus.",
293
399
  "write_text and key inject through the KERNAL keyboard queue ($0277/$00C6) so they work on both C64U and VICE.",
294
- "Joystick tap is suitable for one-shot moves; use press/release pairs for timed holds. Joystick is VICE-only.",
400
+ "Use keyboard for physical C64 key combinations (including modifiers) on machine:input, and release_all to recover from interrupted holds. It requires a C64U firmware version that provides the endpoint, or U64 3.15+.",
401
+ "Before native input, read c64://platform/status. If machine:input is unavailable or unknown, use key or write_text for ordinary C64 input instead of attempting physical matrix events.",
402
+ "Change C64U/U64/U2 configuration through c64_config REST operations, never by keyboard menu navigation. Use keyboard navigation only where REST has no equivalent, such as the machine code monitor, visual SID editor, or Tool Menu.",
403
+ "Joystick uses machine:input on a C64U firmware version that provides the endpoint or U64 3.15+; VICE supports up/down/left/right/fire through its monitor.",
295
404
  ],
296
405
  tools: [
297
406
  {
298
407
  name: "c64_input",
299
- description: "Keyboard buffer injection (cross-platform) and joystick simulation (VICE only).",
300
- summary: "Types text, taps keys, and simulates joystick movements.",
408
+ description: "Cross-platform PETSCII typing plus native Ultimate keyboard and joystick events.",
409
+ summary: "Types text, sends physical key combinations, and controls joysticks.",
301
410
  inputSchema: discriminatedUnionSchema({
302
- description: "Input operations: write_text, key, joystick.",
411
+ description: "Input operations: write_text, key, keyboard, joystick, release_all, state.",
303
412
  variants: [
304
413
  writeTextArgsSchema.jsonSchema,
305
414
  keyArgsSchema.jsonSchema,
306
415
  joystickArgsSchema.jsonSchema,
416
+ keyboardArgsSchema.jsonSchema,
417
+ releaseAllArgsSchema.jsonSchema,
418
+ inputStateArgsSchema.jsonSchema,
307
419
  ],
308
420
  }),
309
- operationPlatforms: { joystick: ["vice"] },
421
+ operationPlatforms: { joystick: ["c64u", "vice"], keyboard: ["c64u"], release_all: ["c64u"], state: ["c64u"] },
310
422
  tags: ["input", "keyboard", "joystick"],
311
423
  examples: [
312
424
  {
@@ -324,6 +436,11 @@ export const inputModule = defineToolModule({
324
436
  description: "Brief rightward tap on joystick port 2",
325
437
  arguments: { op: "joystick", port: 2, controls: ["right"], action: "tap", durationMs: 80 },
326
438
  },
439
+ {
440
+ name: "Type an uppercase A",
441
+ description: "Tap a physical key chord through Ultimate REST input",
442
+ arguments: { op: "keyboard", inputs: ["left_shift", "a"], transition: "tap" },
443
+ },
327
444
  {
328
445
  name: "Press fire on port 1",
329
446
  description: "Hold fire button on joystick port 1",
@@ -1,6 +1,7 @@
1
1
  import { defineToolModule } from "./types.js";
2
2
  import { objectSchema } from "./schema.js";
3
- import { textResult } from "./responses.js";
3
+ import { jsonResult, textResult } from "./responses.js";
4
+ import { Buffer } from "node:buffer";
4
5
  import { ToolError, ToolExecutionError, toolErrorResult, unknownErrorResult, } from "./errors.js";
5
6
  function createNoArgsSchema(description) {
6
7
  return objectSchema({
@@ -24,12 +25,20 @@ function normaliseFailure(details) {
24
25
  }
25
26
  return { value: details };
26
27
  }
28
+ function isMissingMenuScreenEndpoint(error) {
29
+ return typeof error === "object"
30
+ && error !== null
31
+ && "response" in error
32
+ && error.response?.status === 404;
33
+ }
27
34
  const resetArgsSchema = createNoArgsSchema("No arguments required to reset the C64.");
28
35
  const rebootArgsSchema = createNoArgsSchema("No arguments required to reboot the C64 firmware.");
29
36
  const pauseArgsSchema = createNoArgsSchema("No arguments required to pause the machine.");
30
37
  const resumeArgsSchema = createNoArgsSchema("No arguments required to resume the machine after a pause.");
31
38
  const poweroffArgsSchema = createNoArgsSchema("No arguments required to power off the machine.");
39
+ const powerCycleArgsSchema = createNoArgsSchema("No arguments required to power cycle the active system.");
32
40
  const menuButtonArgsSchema = createNoArgsSchema("No arguments required to toggle the Ultimate menu button.");
41
+ const readMenuScreenArgsSchema = createNoArgsSchema("No arguments required to read the active Ultimate menu screen matrix.");
33
42
  export const machineControlModule = defineToolModule({
34
43
  domain: "machine",
35
44
  summary: "Power, reset, pause/resume, and diagnostic controls for the C64 and Ultimate hardware.",
@@ -54,7 +63,7 @@ export const machineControlModule = defineToolModule({
54
63
  workflowHints: [
55
64
  "Use when the user wants a quick restart without losing power; mention that memory contents may persist.",
56
65
  ],
57
- supportedPlatforms: ["c64u", "vice"],
66
+ supportedPlatforms: ["c64u", "u2", "vice"],
58
67
  async execute(args, ctx) {
59
68
  try {
60
69
  resetArgsSchema.parse(args ?? {});
@@ -92,7 +101,7 @@ export const machineControlModule = defineToolModule({
92
101
  workflowHints: [
93
102
  "Choose reboot when configuration changed or hardware is stuck; warn that it will interrupt any running program.",
94
103
  ],
95
- supportedPlatforms: ["c64u", "vice"],
104
+ supportedPlatforms: ["c64u", "u2", "vice"],
96
105
  async execute(args, ctx) {
97
106
  try {
98
107
  rebootArgsSchema.parse(args ?? {});
@@ -130,7 +139,7 @@ export const machineControlModule = defineToolModule({
130
139
  workflowHints: [
131
140
  "Pause when the user needs a stable memory snapshot; remind them to resume to continue execution.",
132
141
  ],
133
- supportedPlatforms: ["c64u"],
142
+ supportedPlatforms: ["c64u", "u2"],
134
143
  async execute(args, ctx) {
135
144
  try {
136
145
  pauseArgsSchema.parse(args ?? {});
@@ -168,7 +177,7 @@ export const machineControlModule = defineToolModule({
168
177
  workflowHints: [
169
178
  "Call after a pause or diagnostic halt and confirm the machine is running again.",
170
179
  ],
171
- supportedPlatforms: ["c64u"],
180
+ supportedPlatforms: ["c64u", "u2"],
172
181
  async execute(args, ctx) {
173
182
  try {
174
183
  resumeArgsSchema.parse(args ?? {});
@@ -231,6 +240,41 @@ export const machineControlModule = defineToolModule({
231
240
  }
232
241
  },
233
242
  },
243
+ {
244
+ name: "power_cycle",
245
+ description: "Return the active system to a fresh state: C64U/U64 uses verified Tool Menu navigation, U2-family reboots through REST, and managed VICE restarts.",
246
+ summary: "Performs the platform-appropriate full power-cycle equivalent and reports the strategy used.",
247
+ inputSchema: powerCycleArgsSchema.jsonSchema,
248
+ tags: ["power", "reboot", "fresh-start"],
249
+ prerequisites: [],
250
+ examples: [
251
+ { name: "Fresh start", description: "Power cycle the active system", arguments: {} },
252
+ ],
253
+ workflowHints: [
254
+ "Use only when the user explicitly requests a fresh start or power cycle; it interrupts active work.",
255
+ "On C64U/U64, verify Tool Menu navigation through machine:menu_screen before selecting Power Cycle.",
256
+ ],
257
+ supportedPlatforms: ["c64u", "u2", "vice"],
258
+ async execute(args, ctx) {
259
+ try {
260
+ powerCycleArgsSchema.parse(args ?? {});
261
+ ctx.logger.info("Power cycling active backend");
262
+ const result = await ctx.client.powerCycle();
263
+ if (!result.success) {
264
+ throw new ToolExecutionError("Power cycle did not complete", {
265
+ details: normaliseFailure(result.details),
266
+ });
267
+ }
268
+ const details = toRecord(result.details) ?? {};
269
+ return textResult("Power-cycle request completed.", { success: true, details });
270
+ }
271
+ catch (error) {
272
+ if (error instanceof ToolError)
273
+ return toolErrorResult(error);
274
+ return unknownErrorResult(error);
275
+ }
276
+ },
277
+ },
234
278
  {
235
279
  name: "menu_button",
236
280
  description: "Toggle the Ultimate 64 menu button.",
@@ -244,7 +288,7 @@ export const machineControlModule = defineToolModule({
244
288
  workflowHints: [
245
289
  "Use when the user needs to open or close the Ultimate menu; suggest following up with drive operations if relevant.",
246
290
  ],
247
- supportedPlatforms: ["c64u"],
291
+ supportedPlatforms: ["c64u", "u2"],
248
292
  async execute(args, ctx) {
249
293
  try {
250
294
  menuButtonArgsSchema.parse(args ?? {});
@@ -269,5 +313,40 @@ export const machineControlModule = defineToolModule({
269
313
  }
270
314
  },
271
315
  },
316
+ {
317
+ name: "read_menu_screen",
318
+ description: "Read the raw character and colour matrix for the currently visible Ultimate menu screen.",
319
+ summary: "Returns the firmware-defined menu matrix bytes, including text and colour information.",
320
+ inputSchema: readMenuScreenArgsSchema.jsonSchema,
321
+ tags: ["menu", "screen", "diagnostic"],
322
+ prerequisites: ["menu_button"],
323
+ examples: [
324
+ { name: "Inspect active menu", description: "Read the currently visible Ultimate menu", arguments: {} },
325
+ ],
326
+ workflowHints: [
327
+ "Read c64://platform/status first. Call after opening the Ultimate menu; if the endpoint is unavailable or no menu is active, the tool returns fallback guidance instead of a transport error.",
328
+ "The firmware returns an opaque character-and-colour matrix, preserved as base64 without guessing a layout.",
329
+ ],
330
+ supportedPlatforms: ["c64u", "u2"],
331
+ async execute(args, ctx) {
332
+ try {
333
+ readMenuScreenArgsSchema.parse(args ?? {});
334
+ const bytes = await ctx.client.readMenuScreen();
335
+ return jsonResult({
336
+ encoding: "base64",
337
+ byteLength: bytes.length,
338
+ matrix: Buffer.from(bytes).toString("base64"),
339
+ });
340
+ }
341
+ catch (error) {
342
+ if (error instanceof ToolError)
343
+ return toolErrorResult(error);
344
+ if (isMissingMenuScreenEndpoint(error)) {
345
+ return textResult("No menu matrix is available. Open the Ultimate menu and retry; if it remains unavailable, this firmware does not provide machine:menu_screen. Use REST configuration instead of menu navigation where possible.", { success: false, code: "native_menu_screen_unavailable", fallback: "rest_configuration_or_manual_menu" });
346
+ }
347
+ return unknownErrorResult(error);
348
+ }
349
+ },
350
+ },
272
351
  ],
273
352
  });
@@ -685,7 +685,7 @@ export { disassembleArgsSchema, copyMemoryArgsSchema, fillMemoryArgsSchema, sear
685
685
  export const memoryModule = defineToolModule({
686
686
  domain: "memory",
687
687
  summary: "Screen, main memory, and low-level inspection utilities.",
688
- supportedPlatforms: ["c64u", "vice"],
688
+ supportedPlatforms: ["c64u", "u2", "vice"],
689
689
  resources: [
690
690
  "c64://guide/bootstrap",
691
691
  "c64://basic/spec",
@@ -30,7 +30,7 @@ export const metaModule = defineToolModule({
30
30
  summary: "High-level meta tools that orchestrate multiple MCP actions.",
31
31
  resources: ["c64://guide/bootstrap", "c64://assembly/6510-spec"],
32
32
  defaultTags: ["meta", "orchestration", "experimental"],
33
- supportedPlatforms: ["c64u", "vice"],
33
+ supportedPlatforms: ["c64u", "u2", "vice"],
34
34
  workflowHints: [
35
35
  "Use meta tools to reduce round-trips by composing several steps into one.",
36
36
  ],
@@ -668,7 +668,7 @@ export const programRunnersModule = defineToolModule({
668
668
  "Invoke right after you generate BASIC source so it runs on the C64 without extra user steps.",
669
669
  "Ensure the program includes line numbers and uppercase keywords before calling the tool.",
670
670
  ],
671
- supportedPlatforms: ["c64u", "vice"],
671
+ supportedPlatforms: ["c64u", "u2", "vice"],
672
672
  async execute(args, ctx) {
673
673
  return executeUploadRunBasic(args, ctx);
674
674
  },
@@ -693,7 +693,7 @@ export const programRunnersModule = defineToolModule({
693
693
  "Use when the user requests to run new 6502 code; surface any assembler diagnostics in your reply.",
694
694
  "Mention the entry routine or important addresses after execution so the user can continue debugging.",
695
695
  ],
696
- supportedPlatforms: ["c64u", "vice"],
696
+ supportedPlatforms: ["c64u", "u2", "vice"],
697
697
  async execute(args, ctx) {
698
698
  return executeUploadRunAsm(args, ctx);
699
699
  },
@@ -717,7 +717,7 @@ export const programRunnersModule = defineToolModule({
717
717
  "Stage PRG files without running when the user wants to inspect memory first.",
718
718
  "Confirm the Ultimate filesystem path (e.g. //USB0/demo.prg) is accessible before invoking.",
719
719
  ],
720
- supportedPlatforms: ["c64u"],
720
+ supportedPlatforms: ["c64u", "u2"],
721
721
  async execute(args, ctx) {
722
722
  return executeLoadPrg(args, ctx);
723
723
  },
@@ -733,7 +733,7 @@ export const programRunnersModule = defineToolModule({
733
733
  "Call when the user provides a PRG path and expects immediate execution without compiling.",
734
734
  "Mention that firmware issues a RUN so the user knows the machine state changed.",
735
735
  ],
736
- supportedPlatforms: ["c64u", "vice"],
736
+ supportedPlatforms: ["c64u", "u2", "vice"],
737
737
  prerequisites: ["drives_list"],
738
738
  examples: [
739
739
  {
@@ -765,7 +765,7 @@ export const programRunnersModule = defineToolModule({
765
765
  arguments: { path: "//USB0/game.crt" },
766
766
  },
767
767
  ],
768
- supportedPlatforms: ["c64u"],
768
+ supportedPlatforms: ["c64u", "u2"],
769
769
  async execute(args, ctx) {
770
770
  try {
771
771
  const parsed = crtFileArgsSchema.parse(args ?? {});
package/dist/tools/rag.js CHANGED
@@ -181,7 +181,7 @@ export const ragModule = defineToolModule({
181
181
  ],
182
182
  prompts: ["basic-program", "assembly-program"],
183
183
  defaultTags: ["rag", "search"],
184
- supportedPlatforms: ["c64u", "vice"],
184
+ supportedPlatforms: ["c64u", "u2", "vice"],
185
185
  workflowHints: [
186
186
  "Call RAG tools when the user needs references or examples before generating new code.",
187
187
  "Summarise the number of refs returned and suggest follow-up actions like reading specific docs.",
@@ -35,7 +35,7 @@ const executeBatchArgsSchema = objectSchema({
35
35
  export const batchModuleGroup = defineToolModule({
36
36
  domain: "batch",
37
37
  summary: "Execute multiple tool calls in a single MCP request for reduced round-trip overhead.",
38
- supportedPlatforms: ["c64u", "vice"],
38
+ supportedPlatforms: ["c64u", "u2", "vice"],
39
39
  resources: [],
40
40
  prompts: [],
41
41
  defaultTags: ["batch", "orchestration"],
@@ -154,7 +154,7 @@ export const configModuleGroup = defineToolModule({
154
154
  resources: ["c64://guide/bootstrap", "c64://guide/index"],
155
155
  prompts: ["memory-debug"],
156
156
  defaultTags: ["config", "diagnostics"],
157
- supportedPlatforms: ["c64u", "vice"],
157
+ supportedPlatforms: ["c64u", "u2", "vice"],
158
158
  workflowHints: [
159
159
  "List categories before changing values so users can confirm firmware-provided names.",
160
160
  "Mention when operations persist to flash or modify debug registers to highlight impacts.",
@@ -171,12 +171,12 @@ export const configModuleGroup = defineToolModule({
171
171
  }),
172
172
  tags: ["config", "diagnostics", "grouped"],
173
173
  operationPlatforms: {
174
- load_flash: ["c64u"],
175
- save_flash: ["c64u"],
176
- reset_defaults: ["c64u"],
174
+ load_flash: ["c64u", "u2"],
175
+ save_flash: ["c64u", "u2"],
176
+ reset_defaults: ["c64u", "u2"],
177
177
  read_debugreg: ["c64u"],
178
178
  write_debugreg: ["c64u"],
179
- shuffle: ["c64u"],
179
+ shuffle: ["c64u", "u2"],
180
180
  },
181
181
  operationToolNames: {
182
182
  load_flash: "config_load_from_flash",
@@ -251,7 +251,7 @@ const diskOperationHandlers = createOperationHandlers(diskOperations);
251
251
  export const diskModuleGroup = defineToolModule({
252
252
  domain: "storage",
253
253
  summary: "Grouped disk image management, mounting, and discovery tools.",
254
- supportedPlatforms: ["c64u", "vice"],
254
+ supportedPlatforms: ["c64u", "u2", "vice"],
255
255
  resources: ["c64://guide/bootstrap"],
256
256
  prompts: ["drive-management"],
257
257
  defaultTags: ["storage", "drive"],
@@ -269,7 +269,7 @@ export const diskModuleGroup = defineToolModule({
269
269
  variants: diskOperations.map((operation) => operation.schema),
270
270
  }),
271
271
  tags: ["storage", "drive", "grouped"],
272
- operationPlatforms: { file_info: ["c64u"], create_image: ["c64u"], find_and_run: ["c64u"] },
272
+ operationPlatforms: { file_info: ["c64u", "u2"], create_image: ["c64u", "u2"], find_and_run: ["c64u", "u2"] },
273
273
  operationToolNames: {
274
274
  file_info: "file_info",
275
275
  create_image: "create_image",
@@ -33,7 +33,7 @@ const driveOperationHandlers = createOperationHandlers(driveOperations);
33
33
  export const driveModuleGroup = defineToolModule({
34
34
  domain: "drive",
35
35
  summary: "Grouped drive power, reset, ROM, and mode helpers.",
36
- supportedPlatforms: ["c64u", "vice"],
36
+ supportedPlatforms: ["c64u", "u2", "vice"],
37
37
  resources: ["c64://guide/bootstrap"],
38
38
  defaultTags: ["drive", "hardware"],
39
39
  workflowHints: [
@@ -50,7 +50,7 @@ export const driveModuleGroup = defineToolModule({
50
50
  variants: driveOperations.map((operation) => operation.schema),
51
51
  }),
52
52
  tags: ["drive", "hardware", "grouped"],
53
- operationPlatforms: { load_rom: ["c64u"] },
53
+ operationPlatforms: { load_rom: ["c64u", "u2"] },
54
54
  operationToolNames: { load_rom: "drive_load_rom" },
55
55
  examples: [
56
56
  {
@@ -89,7 +89,7 @@ function parseVicState(d011, d016, d018, dd00, d020, d021) {
89
89
  };
90
90
  }
91
91
  const getDisplayStateArgsSchema = objectSchema({
92
- description: "Read VIC-II and CIA2 registers to determine the current graphics mode and memory layout. Works on C64U and VICE; both backends are read through shared memory primitives so the response shape is identical.",
92
+ description: "Read VIC-II and CIA2 registers to determine the current graphics mode and memory layout. The same shared-memory path is used on C64U/U64, U2-family hardware, and VICE, so the response shape is identical.",
93
93
  properties: {
94
94
  op: literalSchema("get_display_state"),
95
95
  },
@@ -212,7 +212,7 @@ export const graphicsModuleGroup = defineToolModule({
212
212
  "Use render helpers for PETSCII text, PETSCII art, sprites, and bitmaps; clarify whether PETSCII art executed or stayed a dry run.",
213
213
  "Mention sprite positions/colours so follow-up memory inspection stays grounded.",
214
214
  ],
215
- supportedPlatforms: ["c64u", "vice"],
215
+ supportedPlatforms: ["c64u", "u2", "vice"],
216
216
  tools: [
217
217
  {
218
218
  name: "c64_graphics",
@@ -27,7 +27,7 @@ const memoryOperations = [
27
27
  },
28
28
  {
29
29
  op: "disassemble",
30
- schema: extendSchemaWithOp("disassemble", disassembleArgsSchema.jsonSchema, { description: "Disassemble a memory region into annotated 6502/6510 instructions, including undocumented opcodes with canonical names. Symbol annotations from `.vs` files are applied when available. Works on both C64U and VICE." }),
30
+ schema: extendSchemaWithOp("disassemble", disassembleArgsSchema.jsonSchema, { description: "Disassemble a memory region into annotated 6502/6510 instructions, including undocumented opcodes with canonical names. Symbol annotations from `.vs` files are applied when available. Works on C64U/U64, U2-family hardware, and VICE." }),
31
31
  handler: groupedMemoryHandlers.disassemble,
32
32
  },
33
33
  {
@@ -60,7 +60,7 @@ const memoryOperationHandlers = createOperationHandlers(memoryOperations);
60
60
  export const memoryModuleGroup = defineToolModule({
61
61
  domain: "memory",
62
62
  summary: "Grouped memory, screen, and polling operations.",
63
- supportedPlatforms: ["c64u", "vice"],
63
+ supportedPlatforms: ["c64u", "u2", "vice"],
64
64
  resources: ["c64://guide/bootstrap", "c64://basic/spec", "c64://assembly/6510-spec"],
65
65
  prompts: ["memory-debug", "basic-program", "assembly-program"],
66
66
  defaultTags: ["memory", "debug"],
@@ -50,7 +50,7 @@ const selectBackendSchema = operationSchema("select", {
50
50
  properties: {
51
51
  backend: {
52
52
  type: "string",
53
- enum: ["c64u", "vice"],
53
+ enum: ["c64u", "u2", "vice"],
54
54
  description: "Backend to activate for subsequent tool calls.",
55
55
  },
56
56
  },
@@ -76,14 +76,16 @@ const platformOperationHandlers = {
76
76
  ctx.client.switchBackend(backend);
77
77
  ctx.setPlatform(backend);
78
78
  const { availableTools, unavailableTools } = describeTargetPlatform(backend);
79
- const switchBackTarget = backend === "c64u" ? "vice" : "c64u";
79
+ const switchBackTarget = availableBackends.find((candidate) => candidate !== backend);
80
80
  const data = {
81
81
  success: true,
82
82
  activeBackend: backend,
83
83
  configuredBackends: availableBackends,
84
84
  availableTools,
85
85
  unavailableTools,
86
- usageHint: `Use c64_select_backend again with backend: "${switchBackTarget}" to switch back.`,
86
+ usageHint: switchBackTarget
87
+ ? `Use c64_select_backend again with backend: "${switchBackTarget}" to switch back.`
88
+ : "No other backend is configured.",
87
89
  };
88
90
  return jsonResult(data, { success: true });
89
91
  },
@@ -91,7 +93,7 @@ const platformOperationHandlers = {
91
93
  export const platformModuleGroup = defineToolModule({
92
94
  domain: "platform",
93
95
  summary: "Runtime backend selection and platform-status coordination.",
94
- supportedPlatforms: ["c64u", "vice"],
96
+ supportedPlatforms: ["c64u", "u2", "vice"],
95
97
  resources: ["c64://platform/status", "c64://guide/bootstrap"],
96
98
  prompts: [],
97
99
  defaultTags: ["platform", "backend", "grouped"],
@@ -101,7 +103,7 @@ export const platformModuleGroup = defineToolModule({
101
103
  tools: [
102
104
  {
103
105
  name: "c64_select_backend",
104
- description: "Switch the active backend between C64U hardware and the VICE emulator at runtime.",
106
+ description: "Switch the active backend between C64U/U64 hardware, U2-family cartridges, and the VICE emulator at runtime.",
105
107
  summary: "Changes the active backend without restarting the MCP server and reports resulting tool availability.",
106
108
  inputSchema: discriminatedUnionSchema({
107
109
  description: "Runtime backend selection operations.",
@@ -12,7 +12,7 @@ const programOperations = [
12
12
  },
13
13
  {
14
14
  op: "run_prg",
15
- schema: extendSchemaWithOp("run_prg", ensureDescriptor(programDescriptorIndex, "run_prg").inputSchema, { description: "Load and execute a PRG from Ultimate-visible storage on c64u or a host-local path on VICE." }),
15
+ schema: extendSchemaWithOp("run_prg", ensureDescriptor(programDescriptorIndex, "run_prg").inputSchema, { description: "Load and execute a PRG from Ultimate-visible storage on C64U/U64 or U2-family hardware, or from a host-local path on VICE." }),
16
16
  handler: async (rawArgs, ctx) => invokeModuleTool(programRunnersModule, "run_prg", rawArgs, ctx),
17
17
  },
18
18
  {
@@ -55,10 +55,10 @@ export const programModule = defineToolModule({
55
55
  defaultTags: ["programs", "execution"],
56
56
  workflowHints: [
57
57
  "Choose BASIC or assembly uploaders based on the language you just generated for the user.",
58
- "Prefer PRG or CRT runners when the user supplies a file path instead of source text; PRG paths are host-local on VICE and Ultimate-visible on c64u.",
59
- "For a quick visible confirmation on VICE and/or C64U, prefer cross_platform_greeting instead of composing manual backend switches and BASIC upload steps.",
58
+ "Prefer PRG or CRT runners when the user supplies a file path instead of source text; PRG paths are host-local on VICE and Ultimate-visible on hardware.",
59
+ "For a quick visible confirmation on VICE, C64U/U64, or U2-family hardware, prefer cross_platform_greeting instead of composing manual backend switches and BASIC upload steps.",
60
60
  ],
61
- supportedPlatforms: ["c64u", "vice"],
61
+ supportedPlatforms: ["c64u", "u2", "vice"],
62
62
  tools: [
63
63
  {
64
64
  name: "c64_program",
@@ -69,7 +69,7 @@ export const programModule = defineToolModule({
69
69
  variants: programOperations.map((operation) => operation.schema),
70
70
  }),
71
71
  tags: ["programs", "execution", "grouped"],
72
- operationPlatforms: { load_prg: ["c64u"], run_crt: ["c64u"], bundle_run: ["c64u"] },
72
+ operationPlatforms: { load_prg: ["c64u", "u2"], run_crt: ["c64u", "u2"], bundle_run: ["c64u", "u2"] },
73
73
  operationToolNames: {
74
74
  load_prg: "load_prg",
75
75
  run_crt: "run_crt",