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.
- package/AGENTS.md +19 -3
- package/README.md +190 -148
- package/dist/.tsbuildinfo +1 -1
- package/dist/c64Client.js +127 -5
- package/dist/config.js +12 -8
- package/dist/device.js +75 -45
- package/dist/mcp-server.js +11 -3
- package/dist/platform.js +13 -2
- package/dist/tools/audio.js +1 -1
- package/dist/tools/developer.js +6 -6
- package/dist/tools/graphics.js +4 -4
- package/dist/tools/input.js +129 -12
- package/dist/tools/machineControl.js +85 -6
- package/dist/tools/memory.js +1 -1
- package/dist/tools/meta/index.js +1 -1
- package/dist/tools/programRunners.js +5 -5
- package/dist/tools/rag.js +1 -1
- package/dist/tools/registry/batch.js +1 -1
- package/dist/tools/registry/config.js +5 -5
- package/dist/tools/registry/disk.js +2 -2
- package/dist/tools/registry/drive.js +2 -2
- package/dist/tools/registry/graphics.js +2 -2
- package/dist/tools/registry/memory.js +2 -2
- package/dist/tools/registry/platform.js +7 -5
- package/dist/tools/registry/program.js +5 -5
- package/dist/tools/registry/rag.js +1 -1
- package/dist/tools/registry/sound.js +4 -4
- package/dist/tools/registry/system.js +13 -3
- package/dist/tools/storage.js +1 -1
- package/doc/c64u/c64-openapi.yaml +1130 -774
- package/doc/u2/u2-openapi.yaml +1192 -0
- package/generated/c64/index.js +142 -9
- package/generated/openapi/c64u/index.js +1117 -0
- package/generated/openapi/u2/index.js +929 -0
- package/mcp/schemas/c64_graphics.schema.json +3 -3
- package/mcp/schemas/c64_input.schema.json +247 -6
- package/mcp/schemas/c64_memory.schema.json +3 -3
- package/mcp/schemas/c64_program.schema.json +3 -3
- package/mcp/schemas/c64_select_backend.schema.json +2 -0
- package/mcp/schemas/c64_system.schema.json +41 -1
- package/mcp/server.json +1 -1
- package/mcp/tools.json +382 -41
- package/mcp.json +14 -2
- package/package.json +4 -2
- package/scripts/mockC64Server.mjs +40 -0
- package/scripts/prepare-release.mjs +13 -2
- package/scripts/update-readme.ts +18 -30
- package/server.json +3 -3
package/dist/tools/input.js
CHANGED
|
@@ -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
|
|
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:
|
|
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: "
|
|
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;
|
|
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
|
-
"
|
|
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: "
|
|
300
|
-
summary: "Types text,
|
|
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
|
});
|
package/dist/tools/memory.js
CHANGED
|
@@ -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",
|
package/dist/tools/meta/index.js
CHANGED
|
@@ -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.
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
|
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
|
|
59
|
-
"For a quick visible confirmation on VICE
|
|
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",
|