c64bridge 0.7.4 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/prompts/README.md +14 -4
- package/.github/prompts/assembly-routine-session.prompt.md +9 -9
- package/.github/prompts/basic-program-session.prompt.md +11 -9
- package/.github/prompts/drive-management-session.prompt.md +9 -9
- package/.github/prompts/hello-world-session.prompt.md +17 -0
- package/.github/prompts/petscii-art-session.prompt.md +9 -9
- package/.github/prompts/play-classic-game.prompt.md +9 -9
- package/.github/prompts/pr-converge.prompt.md +13 -0
- package/.github/prompts/sid-music-session.prompt.md +9 -9
- package/AGENTS.md +59 -60
- package/README.md +550 -191
- package/data/assembly/assembly-spec.md +290 -1
- package/data/context/bootstrap.md +26 -2
- package/data/context/fast-paths.md +44 -0
- package/data/graphics/petscii-style-guide.md +6 -2
- package/data/graphics/sprite-charset-best-practices.md +5 -5
- package/dist/.tsbuildinfo +1 -1
- package/dist/c64Client.js +990 -26
- package/dist/config.js +5 -1
- package/dist/device.js +680 -57
- package/dist/diagnostics.js +371 -0
- package/dist/mcp/metadata.js +39 -0
- package/dist/mcp-server.js +170 -20
- package/dist/platform.js +10 -2
- package/dist/prompts/registry.js +166 -309
- package/dist/rag/init.js +37 -1
- package/dist/rag/knowledgeIndex.js +18 -6
- package/dist/rag/rateLimiter.js +2 -2
- package/dist/streamCapture.js +135 -0
- package/dist/tools/audio.js +3 -1
- package/dist/tools/debug.js +470 -0
- package/dist/tools/developer.js +10 -4
- package/dist/tools/graphics.js +183 -17
- package/dist/tools/machineControl.js +11 -5
- package/dist/tools/memory.js +19 -10
- package/dist/tools/meta/audio.js +240 -1
- package/dist/tools/meta/config.js +20 -28
- package/dist/tools/meta/configInventory.js +104 -0
- package/dist/tools/meta/diagnostics.js +45 -2
- package/dist/tools/meta/graphics.js +14 -6
- package/dist/tools/meta/index.js +1 -0
- package/dist/tools/meta/memory.js +20 -6
- package/dist/tools/meta/program.js +473 -0
- package/dist/tools/pollValidator.js +4 -2
- package/dist/tools/programRunners.js +8 -4
- package/dist/tools/registry/config.js +17 -0
- package/dist/tools/registry/disk.js +7 -0
- package/dist/tools/registry/drive.js +3 -0
- package/dist/tools/registry/graphics.js +111 -27
- package/dist/tools/registry/index.js +6 -0
- package/dist/tools/registry/memory.js +1 -0
- package/dist/tools/registry/platform.js +121 -0
- package/dist/tools/registry/program.js +26 -8
- package/dist/tools/registry/sound.js +96 -0
- package/dist/tools/registry/system.js +15 -2
- package/dist/tools/storage.js +1 -0
- package/dist/tools/streaming.js +1 -0
- package/dist/tools/types.js +38 -4
- package/dist/tools/vice.js +160 -0
- package/dist/vicBitmap.js +317 -0
- package/dist/vice/mockServer.js +439 -0
- package/dist/vice/process.js +310 -0
- package/dist/vice/readiness.js +18 -2
- package/dist/vice/viceClient.js +382 -22
- package/doc/developer.md +71 -25
- package/doc/img/backend-switch/hello-c64u.png +0 -0
- package/doc/img/backend-switch/hello-vice-and-c64u.png +0 -0
- package/doc/img/backend-switch/hello-vice.png +0 -0
- package/doc/img/backend-switch/metadata.json +50 -0
- package/doc/plans/vice-backend-switch/plan.md +486 -0
- package/doc/plans/vice-backend-switch-2/plan.md +48 -0
- package/doc/plans/vice-support/notes.md +1 -0
- package/doc/plans/vice-support/plan.md +5 -0
- package/doc/plans/vice-support/rollout.md +23 -17
- package/doc/reviews/vice-support-review.md +185 -0
- package/doc/troubleshooting-mcp.md +35 -6
- package/doc/vice/support-matrix.md +54 -0
- package/mcp/prompts.json +292 -0
- package/mcp/protocol-examples.json +46 -0
- package/mcp/resources.json +940 -0
- package/mcp/schemas/c64_config.schema.json +350 -0
- package/mcp/schemas/c64_debug.schema.json +380 -0
- package/mcp/schemas/c64_disk.schema.json +278 -0
- package/mcp/schemas/c64_drive.schema.json +120 -0
- package/mcp/schemas/c64_extract.schema.json +302 -0
- package/mcp/schemas/c64_graphics.schema.json +333 -0
- package/mcp/schemas/c64_memory.schema.json +155 -0
- package/mcp/schemas/c64_printer.schema.json +218 -0
- package/mcp/schemas/c64_program.schema.json +391 -0
- package/mcp/schemas/c64_rag.schema.json +67 -0
- package/mcp/schemas/c64_select_backend.schema.json +32 -0
- package/mcp/schemas/c64_sound.schema.json +644 -0
- package/mcp/schemas/c64_stream.schema.json +61 -0
- package/mcp/schemas/c64_system.schema.json +227 -0
- package/mcp/schemas/c64_vice.schema.json +63 -0
- package/mcp/server.json +15 -0
- package/mcp/tools.json +4517 -0
- package/mcp.json +90 -15
- package/package.json +11 -4
- package/scripts/capture-backend-switch-screenshots.mjs +276 -0
- package/scripts/generate-mcp-interface.ts +289 -0
- package/scripts/invoke-bun.mjs +67 -21
- package/scripts/mockC64Server.mjs +162 -6
- package/scripts/prepare-release.mjs +59 -2
- package/scripts/run-coverage.mjs +284 -0
- package/scripts/run-tests.mjs +95 -39
- package/scripts/run-tests.ts +378 -73
- package/scripts/update-readme.ts +147 -13
- package/server.json +94 -0
|
@@ -1,74 +1,158 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
1
2
|
import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema, } from "../types.js";
|
|
2
3
|
import { graphicsModule, graphicsOperationHandlers as groupedGraphicsHandlers } from "../graphics.js";
|
|
4
|
+
import { booleanSchema, literalSchema, numberSchema, objectSchema, optionalSchema, stringSchema, } from "../schema.js";
|
|
3
5
|
import { buildDescriptorIndex, ensureDescriptor, extendSchemaWithOp, createOperationHandlers, } from "./utils.js";
|
|
4
|
-
import {
|
|
6
|
+
import { ToolError, toolErrorResult, unknownErrorResult, } from "../errors.js";
|
|
7
|
+
import { jsonResult } from "../responses.js";
|
|
5
8
|
const graphicsDescriptorIndex = buildDescriptorIndex(graphicsModule);
|
|
9
|
+
const captureFrameArgsSchema = objectSchema({
|
|
10
|
+
description: "Capture one or more complete video frames from the active backend.",
|
|
11
|
+
properties: {
|
|
12
|
+
op: literalSchema("capture_frame"),
|
|
13
|
+
count: optionalSchema(numberSchema({
|
|
14
|
+
description: "Number of frames to capture.",
|
|
15
|
+
integer: true,
|
|
16
|
+
minimum: 1,
|
|
17
|
+
maximum: 32,
|
|
18
|
+
default: 1,
|
|
19
|
+
}), 1),
|
|
20
|
+
includePixels: optionalSchema(booleanSchema({
|
|
21
|
+
description: "Include pixel payload bytes in the response.",
|
|
22
|
+
default: true,
|
|
23
|
+
}), true),
|
|
24
|
+
encoding: optionalSchema(stringSchema({
|
|
25
|
+
description: "Encoding used for the pixel payload when included.",
|
|
26
|
+
enum: ["base64", "hex"],
|
|
27
|
+
default: "base64",
|
|
28
|
+
}), "base64"),
|
|
29
|
+
},
|
|
30
|
+
required: ["op"],
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
});
|
|
6
33
|
const graphicsOperations = [
|
|
7
34
|
{
|
|
8
|
-
op: "
|
|
9
|
-
schema:
|
|
10
|
-
handler:
|
|
35
|
+
op: "capture_frame",
|
|
36
|
+
schema: captureFrameArgsSchema.jsonSchema,
|
|
37
|
+
handler: async (rawArgs, ctx) => {
|
|
38
|
+
try {
|
|
39
|
+
const parsed = captureFrameArgsSchema.parse(rawArgs);
|
|
40
|
+
ctx.logger.info("Capturing frame buffer", {
|
|
41
|
+
count: parsed.count,
|
|
42
|
+
includePixels: parsed.includePixels,
|
|
43
|
+
encoding: parsed.encoding,
|
|
44
|
+
});
|
|
45
|
+
const capture = await ctx.client.captureFrames({ count: parsed.count });
|
|
46
|
+
const includePixels = parsed.includePixels !== false;
|
|
47
|
+
const encoding = parsed.encoding ?? "base64";
|
|
48
|
+
const frames = capture.frames.map((frame, index) => {
|
|
49
|
+
const pixels = includePixels
|
|
50
|
+
? {
|
|
51
|
+
encoding,
|
|
52
|
+
data: encoding === "hex"
|
|
53
|
+
? Buffer.from(frame.pixels).toString("hex")
|
|
54
|
+
: Buffer.from(frame.pixels).toString("base64"),
|
|
55
|
+
}
|
|
56
|
+
: undefined;
|
|
57
|
+
return {
|
|
58
|
+
index,
|
|
59
|
+
frameNumber: frame.frameNumber,
|
|
60
|
+
width: frame.width,
|
|
61
|
+
height: frame.height,
|
|
62
|
+
bitsPerPixel: frame.bitsPerPixel,
|
|
63
|
+
byteLength: frame.pixels.length,
|
|
64
|
+
complete: frame.complete,
|
|
65
|
+
...(pixels ? { pixels } : {}),
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
return jsonResult({
|
|
69
|
+
backend: capture.backend,
|
|
70
|
+
count: frames.length,
|
|
71
|
+
frames,
|
|
72
|
+
}, {
|
|
73
|
+
success: true,
|
|
74
|
+
backend: capture.backend,
|
|
75
|
+
count: frames.length,
|
|
76
|
+
includePixels,
|
|
77
|
+
encoding: includePixels ? encoding : null,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error instanceof ToolError) {
|
|
82
|
+
return toolErrorResult(error);
|
|
83
|
+
}
|
|
84
|
+
return unknownErrorResult(error);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
op: "render_petscii_art",
|
|
90
|
+
schema: extendSchemaWithOp("render_petscii_art", ensureDescriptor(graphicsDescriptorIndex, "render_petscii_art").inputSchema, { description: "Create PETSCII art from prompts, text, or explicit bitmap data, and optionally display it on the C64." }),
|
|
91
|
+
handler: groupedGraphicsHandlers.render_petscii_art,
|
|
11
92
|
},
|
|
12
93
|
{
|
|
13
|
-
op: "
|
|
14
|
-
schema: extendSchemaWithOp("
|
|
15
|
-
handler: groupedGraphicsHandlers.
|
|
94
|
+
op: "render_petscii_text",
|
|
95
|
+
schema: extendSchemaWithOp("render_petscii_text", ensureDescriptor(graphicsDescriptorIndex, "render_petscii_text").inputSchema, { description: "Display PETSCII text with optional border and background colours." }),
|
|
96
|
+
handler: groupedGraphicsHandlers.render_petscii_text,
|
|
16
97
|
},
|
|
17
98
|
{
|
|
18
|
-
op: "
|
|
19
|
-
schema: extendSchemaWithOp("
|
|
20
|
-
handler: groupedGraphicsHandlers.
|
|
99
|
+
op: "render_sprite",
|
|
100
|
+
schema: extendSchemaWithOp("render_sprite", ensureDescriptor(graphicsDescriptorIndex, "render_sprite").inputSchema, { description: "Display supplied 63-byte sprite data at the requested position and colour by writing memory and patching VIC-II registers directly." }),
|
|
101
|
+
handler: groupedGraphicsHandlers.render_sprite,
|
|
21
102
|
},
|
|
22
103
|
{
|
|
23
|
-
op: "
|
|
24
|
-
schema: extendSchemaWithOp("
|
|
25
|
-
|
|
26
|
-
type: "object",
|
|
27
|
-
properties: {},
|
|
28
|
-
additionalProperties: false,
|
|
29
|
-
}, { description: "Reserved high-resolution bitmap generator (coming soon)." }),
|
|
30
|
-
handler: async () => toolErrorResult(new ToolExecutionError("c64_graphics op generate_bitmap is not yet available", {
|
|
31
|
-
details: { available: false },
|
|
32
|
-
})),
|
|
104
|
+
op: "render_bitmap",
|
|
105
|
+
schema: extendSchemaWithOp("render_bitmap", ensureDescriptor(graphicsDescriptorIndex, "render_bitmap").inputSchema, { description: "Import an image file, convert it to VIC-II bitmap memory, write it into RAM, and display it." }),
|
|
106
|
+
handler: groupedGraphicsHandlers.render_bitmap,
|
|
33
107
|
},
|
|
34
108
|
];
|
|
35
109
|
const graphicsOperationHandlers = createOperationHandlers(graphicsOperations);
|
|
36
110
|
export const graphicsModuleGroup = defineToolModule({
|
|
37
111
|
domain: "graphics",
|
|
38
|
-
summary: "Grouped
|
|
112
|
+
summary: "Grouped frame capture and graphics rendering helpers.",
|
|
39
113
|
resources: ["c64://specs/vic", "c64://specs/basic", "c64://specs/assembly"],
|
|
40
114
|
prompts: ["graphics-demo", "basic-program", "assembly-program"],
|
|
41
115
|
defaultTags: ["graphics", "vic"],
|
|
42
116
|
workflowHints: [
|
|
43
|
-
"Use
|
|
117
|
+
"Use render helpers for PETSCII text, PETSCII art, sprites, and bitmaps; clarify whether PETSCII art executed or stayed a dry run.",
|
|
44
118
|
"Mention sprite positions/colours so follow-up memory inspection stays grounded.",
|
|
45
119
|
],
|
|
46
120
|
supportedPlatforms: ["c64u", "vice"],
|
|
47
121
|
tools: [
|
|
48
122
|
{
|
|
49
123
|
name: "c64_graphics",
|
|
50
|
-
description: "Grouped entry point for
|
|
51
|
-
summary: "
|
|
124
|
+
description: "Grouped entry point for frame capture and graphics rendering workflows.",
|
|
125
|
+
summary: "Captures frames, renders PETSCII text or art, previews sprites, and displays bitmap images from one tool.",
|
|
52
126
|
inputSchema: discriminatedUnionSchema({
|
|
53
127
|
description: "Graphics operations available via the c64_graphics tool.",
|
|
54
128
|
variants: graphicsOperations.map((operation) => operation.schema),
|
|
55
129
|
}),
|
|
56
130
|
tags: ["graphics", "vic", "grouped"],
|
|
57
131
|
examples: [
|
|
132
|
+
{
|
|
133
|
+
name: "Capture one frame",
|
|
134
|
+
description: "Grab the current framebuffer from the active backend",
|
|
135
|
+
arguments: { op: "capture_frame" },
|
|
136
|
+
},
|
|
58
137
|
{
|
|
59
138
|
name: "Create PETSCII art (dry run)",
|
|
60
139
|
description: "Synthesize art without uploading to the C64",
|
|
61
|
-
arguments: { op: "
|
|
140
|
+
arguments: { op: "render_petscii_art", prompt: "duck on a pond", dryRun: true },
|
|
62
141
|
},
|
|
63
142
|
{
|
|
64
143
|
name: "Render PETSCII text",
|
|
65
144
|
description: "Print HELLO with blue border",
|
|
66
|
-
arguments: { op: "
|
|
145
|
+
arguments: { op: "render_petscii_text", text: "HELLO", borderColor: 6 },
|
|
67
146
|
},
|
|
68
147
|
{
|
|
69
148
|
name: "Display sprite",
|
|
70
149
|
description: "Show sprite data at coordinates",
|
|
71
|
-
arguments: { op: "
|
|
150
|
+
arguments: { op: "render_sprite", sprite: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" },
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: "Import bitmap image",
|
|
154
|
+
description: "Convert a PNG into VIC bitmap memory and display it",
|
|
155
|
+
arguments: { op: "render_bitmap", imagePath: "./artifacts/sample.png", format: "hires" },
|
|
72
156
|
},
|
|
73
157
|
],
|
|
74
158
|
execute: createOperationDispatcher("c64_graphics", graphicsOperationHandlers),
|
|
@@ -3,6 +3,7 @@ import { programModule } from "./program.js";
|
|
|
3
3
|
import { memoryModuleGroup as memoryModule } from "./memory.js";
|
|
4
4
|
import { soundModuleGroup as soundModule } from "./sound.js";
|
|
5
5
|
import { systemModuleGroup as systemModule } from "./system.js";
|
|
6
|
+
import { platformModuleGroup } from "./platform.js";
|
|
6
7
|
import { graphicsModuleGroup as graphicsModule } from "./graphics.js";
|
|
7
8
|
import { ragModuleGroup as ragModule } from "./rag.js";
|
|
8
9
|
import { diskModuleGroup as diskModule } from "./disk.js";
|
|
@@ -11,11 +12,14 @@ import { printerModuleGroup as printerModule } from "./printer.js";
|
|
|
11
12
|
import { configModuleGroup as configModule } from "./config.js";
|
|
12
13
|
import { extractModule } from "./extract.js";
|
|
13
14
|
import { streamModule } from "./stream.js";
|
|
15
|
+
import { debugModuleGroup as debugModule } from "../debug.js";
|
|
16
|
+
import { viceModuleGroup as viceModule } from "../vice.js";
|
|
14
17
|
const modules = [
|
|
15
18
|
programModule,
|
|
16
19
|
memoryModule,
|
|
17
20
|
soundModule,
|
|
18
21
|
systemModule,
|
|
22
|
+
platformModuleGroup,
|
|
19
23
|
graphicsModule,
|
|
20
24
|
ragModule,
|
|
21
25
|
diskModule,
|
|
@@ -24,6 +28,8 @@ const modules = [
|
|
|
24
28
|
configModule,
|
|
25
29
|
extractModule,
|
|
26
30
|
streamModule,
|
|
31
|
+
debugModule,
|
|
32
|
+
viceModule,
|
|
27
33
|
];
|
|
28
34
|
const toolMap = new Map();
|
|
29
35
|
for (const module of modules) {
|
|
@@ -30,6 +30,7 @@ const memoryOperationHandlers = createOperationHandlers(memoryOperations);
|
|
|
30
30
|
export const memoryModuleGroup = defineToolModule({
|
|
31
31
|
domain: "memory",
|
|
32
32
|
summary: "Grouped memory, screen, and polling operations.",
|
|
33
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
33
34
|
resources: ["c64://context/bootstrap", "c64://specs/basic", "c64://specs/assembly"],
|
|
34
35
|
prompts: ["memory-debug", "basic-program", "assembly-program"],
|
|
35
36
|
defaultTags: ["memory", "debug"],
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describePlatformCapabilities } from "../../platform.js";
|
|
2
|
+
import { debugModuleGroup as debugModule } from "../debug.js";
|
|
3
|
+
import { viceModuleGroup as viceModule } from "../vice.js";
|
|
4
|
+
import { jsonResult } from "../responses.js";
|
|
5
|
+
import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema, operationSchema, } from "../types.js";
|
|
6
|
+
import { configModuleGroup as configModule } from "./config.js";
|
|
7
|
+
import { diskModuleGroup as diskModule } from "./disk.js";
|
|
8
|
+
import { driveModuleGroup as driveModule } from "./drive.js";
|
|
9
|
+
import { extractModule } from "./extract.js";
|
|
10
|
+
import { graphicsModuleGroup as graphicsModule } from "./graphics.js";
|
|
11
|
+
import { memoryModuleGroup as memoryModule } from "./memory.js";
|
|
12
|
+
import { printerModuleGroup as printerModule } from "./printer.js";
|
|
13
|
+
import { programModule } from "./program.js";
|
|
14
|
+
import { ragModuleGroup as ragModule } from "./rag.js";
|
|
15
|
+
import { soundModuleGroup as soundModule } from "./sound.js";
|
|
16
|
+
import { streamModule } from "./stream.js";
|
|
17
|
+
import { systemModuleGroup as systemModule } from "./system.js";
|
|
18
|
+
const registryModules = [
|
|
19
|
+
programModule,
|
|
20
|
+
memoryModule,
|
|
21
|
+
soundModule,
|
|
22
|
+
systemModule,
|
|
23
|
+
graphicsModule,
|
|
24
|
+
ragModule,
|
|
25
|
+
diskModule,
|
|
26
|
+
driveModule,
|
|
27
|
+
printerModule,
|
|
28
|
+
configModule,
|
|
29
|
+
extractModule,
|
|
30
|
+
streamModule,
|
|
31
|
+
debugModule,
|
|
32
|
+
viceModule,
|
|
33
|
+
];
|
|
34
|
+
function listRegisteredDescriptors() {
|
|
35
|
+
return [
|
|
36
|
+
...registryModules.flatMap((module) => module.describeTools()),
|
|
37
|
+
...platformModuleGroup.describeTools(),
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
function describeTargetPlatform(target) {
|
|
41
|
+
const capabilities = describePlatformCapabilities(listRegisteredDescriptors());
|
|
42
|
+
const info = capabilities.platforms[target];
|
|
43
|
+
return {
|
|
44
|
+
availableTools: info.tools,
|
|
45
|
+
unavailableTools: info.unsupported_tools,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const selectBackendSchema = operationSchema("select", {
|
|
49
|
+
description: "Switch the active runtime backend without restarting the MCP server.",
|
|
50
|
+
properties: {
|
|
51
|
+
backend: {
|
|
52
|
+
type: "string",
|
|
53
|
+
enum: ["c64u", "vice"],
|
|
54
|
+
description: "Backend to activate for subsequent tool calls.",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
required: ["backend"],
|
|
58
|
+
});
|
|
59
|
+
const platformOperationHandlers = {
|
|
60
|
+
async select(args, ctx) {
|
|
61
|
+
const backend = args.backend;
|
|
62
|
+
const availableBackends = ctx.client.getAvailableBackends().slice().sort();
|
|
63
|
+
if (!availableBackends.includes(backend)) {
|
|
64
|
+
const data = {
|
|
65
|
+
success: false,
|
|
66
|
+
requestedBackend: backend,
|
|
67
|
+
configuredBackends: availableBackends,
|
|
68
|
+
message: `Backend '${backend}' is not configured. Available backends: ${availableBackends.join(", ") || "none"}.`,
|
|
69
|
+
usageHint: "Use c64_select_backend with one of the configured backends when you want to switch later.",
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
...jsonResult(data, { success: false }),
|
|
73
|
+
isError: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
ctx.client.switchBackend(backend);
|
|
77
|
+
ctx.setPlatform(backend);
|
|
78
|
+
const { availableTools, unavailableTools } = describeTargetPlatform(backend);
|
|
79
|
+
const switchBackTarget = backend === "c64u" ? "vice" : "c64u";
|
|
80
|
+
const data = {
|
|
81
|
+
success: true,
|
|
82
|
+
activeBackend: backend,
|
|
83
|
+
configuredBackends: availableBackends,
|
|
84
|
+
availableTools,
|
|
85
|
+
unavailableTools,
|
|
86
|
+
usageHint: `Use c64_select_backend again with backend: "${switchBackTarget}" to switch back.`,
|
|
87
|
+
};
|
|
88
|
+
return jsonResult(data, { success: true });
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
export const platformModuleGroup = defineToolModule({
|
|
92
|
+
domain: "platform",
|
|
93
|
+
summary: "Runtime backend selection and platform-status coordination.",
|
|
94
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
95
|
+
resources: ["c64://platform/status", "c64://context/bootstrap"],
|
|
96
|
+
prompts: [],
|
|
97
|
+
defaultTags: ["platform", "backend", "grouped"],
|
|
98
|
+
workflowHints: [
|
|
99
|
+
"Switch backends before invoking backend-specific tools so platform gating stays accurate.",
|
|
100
|
+
],
|
|
101
|
+
tools: [
|
|
102
|
+
{
|
|
103
|
+
name: "c64_select_backend",
|
|
104
|
+
description: "Switch the active backend between C64U hardware and the VICE emulator at runtime.",
|
|
105
|
+
summary: "Changes the active backend without restarting the MCP server and reports resulting tool availability.",
|
|
106
|
+
inputSchema: discriminatedUnionSchema({
|
|
107
|
+
description: "Runtime backend selection operations.",
|
|
108
|
+
variants: [selectBackendSchema],
|
|
109
|
+
}),
|
|
110
|
+
tags: ["platform", "backend", "switch"],
|
|
111
|
+
execute: createOperationDispatcher("c64_select_backend", platformOperationHandlers),
|
|
112
|
+
examples: [
|
|
113
|
+
{
|
|
114
|
+
name: "Switch to VICE",
|
|
115
|
+
description: "Move subsequent tool calls to the emulator backend",
|
|
116
|
+
arguments: { op: "select", backend: "vice" },
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema, } from "../types.js";
|
|
2
|
-
import { programRunnersModule
|
|
2
|
+
import { programRunnersModule } from "../programRunners.js";
|
|
3
3
|
import { metaModule } from "../meta/index.js";
|
|
4
4
|
import { buildDescriptorIndex, ensureDescriptor, extendSchemaWithOp, createOperationHandlers, invokeModuleTool, } from "./utils.js";
|
|
5
5
|
const programDescriptorIndex = buildDescriptorIndex(programRunnersModule);
|
|
@@ -8,27 +8,27 @@ const programOperations = [
|
|
|
8
8
|
{
|
|
9
9
|
op: "load_prg",
|
|
10
10
|
schema: extendSchemaWithOp("load_prg", ensureDescriptor(programDescriptorIndex, "load_prg").inputSchema, { description: "Load a PRG from Ultimate storage without executing it." }),
|
|
11
|
-
handler:
|
|
11
|
+
handler: async (rawArgs, ctx) => invokeModuleTool(programRunnersModule, "load_prg", rawArgs, ctx),
|
|
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
|
|
16
|
-
handler:
|
|
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." }),
|
|
16
|
+
handler: async (rawArgs, ctx) => invokeModuleTool(programRunnersModule, "run_prg", rawArgs, ctx),
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
op: "run_crt",
|
|
20
20
|
schema: extendSchemaWithOp("run_crt", ensureDescriptor(programDescriptorIndex, "run_crt").inputSchema, { description: "Mount and run a CRT cartridge image." }),
|
|
21
|
-
handler:
|
|
21
|
+
handler: async (rawArgs, ctx) => invokeModuleTool(programRunnersModule, "run_crt", rawArgs, ctx),
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
op: "upload_run_basic",
|
|
25
25
|
schema: extendSchemaWithOp("upload_run_basic", ensureDescriptor(programDescriptorIndex, "upload_run_basic").inputSchema, { description: "Upload Commodore BASIC v2 source and execute it immediately." }),
|
|
26
|
-
handler:
|
|
26
|
+
handler: async (rawArgs, ctx) => invokeModuleTool(programRunnersModule, "upload_run_basic", rawArgs, ctx),
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
op: "upload_run_asm",
|
|
30
30
|
schema: extendSchemaWithOp("upload_run_asm", ensureDescriptor(programDescriptorIndex, "upload_run_asm").inputSchema, { description: "Assemble 6502/6510 source, upload the PRG, and execute it." }),
|
|
31
|
-
handler:
|
|
31
|
+
handler: async (rawArgs, ctx) => invokeModuleTool(programRunnersModule, "upload_run_asm", rawArgs, ctx),
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
op: "batch_run",
|
|
@@ -40,6 +40,11 @@ const programOperations = [
|
|
|
40
40
|
schema: extendSchemaWithOp("bundle_run", ensureDescriptor(metaDescriptorIndex, "bundle_run_artifacts").inputSchema, { description: "Capture screen, memory, and debug registers into an artifact bundle." }),
|
|
41
41
|
handler: async (rawArgs, ctx) => invokeModuleTool(metaModule, "bundle_run_artifacts", rawArgs, ctx),
|
|
42
42
|
},
|
|
43
|
+
{
|
|
44
|
+
op: "cross_platform_greeting",
|
|
45
|
+
schema: extendSchemaWithOp("cross_platform_greeting", ensureDescriptor(metaDescriptorIndex, "cross_platform_greeting").inputSchema, { description: "Show a platform-customized greeting on one or more configured backends, capture screenshots, and verify the results." }),
|
|
46
|
+
handler: async (rawArgs, ctx) => invokeModuleTool(metaModule, "cross_platform_greeting", rawArgs, ctx),
|
|
47
|
+
},
|
|
43
48
|
];
|
|
44
49
|
const programOperationHandlers = createOperationHandlers(programOperations);
|
|
45
50
|
export const programModule = defineToolModule({
|
|
@@ -50,7 +55,8 @@ export const programModule = defineToolModule({
|
|
|
50
55
|
defaultTags: ["programs", "execution"],
|
|
51
56
|
workflowHints: [
|
|
52
57
|
"Choose BASIC or assembly uploaders based on the language you just generated for the user.",
|
|
53
|
-
"Prefer PRG or CRT runners when the user supplies
|
|
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.",
|
|
54
60
|
],
|
|
55
61
|
supportedPlatforms: ["c64u", "vice"],
|
|
56
62
|
tools: [
|
|
@@ -63,6 +69,13 @@ export const programModule = defineToolModule({
|
|
|
63
69
|
variants: programOperations.map((operation) => operation.schema),
|
|
64
70
|
}),
|
|
65
71
|
tags: ["programs", "execution", "grouped"],
|
|
72
|
+
operationPlatforms: { load_prg: ["c64u"], run_crt: ["c64u"], bundle_run: ["c64u"] },
|
|
73
|
+
operationToolNames: {
|
|
74
|
+
load_prg: "load_prg",
|
|
75
|
+
run_crt: "run_crt",
|
|
76
|
+
bundle_run: "bundle_run_artifacts",
|
|
77
|
+
cross_platform_greeting: "cross_platform_greeting",
|
|
78
|
+
},
|
|
66
79
|
examples: [
|
|
67
80
|
{
|
|
68
81
|
name: "Run PRG from storage",
|
|
@@ -74,6 +87,11 @@ export const programModule = defineToolModule({
|
|
|
74
87
|
description: "Send inline BASIC to the C64 and run it",
|
|
75
88
|
arguments: { op: "upload_run_basic", program: "10 PRINT \"HELLO\"\n20 GOTO 10" },
|
|
76
89
|
},
|
|
90
|
+
{
|
|
91
|
+
name: "Show greeting on both backends",
|
|
92
|
+
description: "Render a platform-specific greeting on VICE and C64U with screenshot verification",
|
|
93
|
+
arguments: { op: "cross_platform_greeting" },
|
|
94
|
+
},
|
|
77
95
|
],
|
|
78
96
|
execute: createOperationDispatcher("c64_program", programOperationHandlers),
|
|
79
97
|
},
|
|
@@ -1,10 +1,74 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
1
2
|
import { createOperationDispatcher, defineToolModule, discriminatedUnionSchema, } from "../types.js";
|
|
2
3
|
import { audioModule } from "../audio.js";
|
|
3
4
|
import { metaModule } from "../meta/index.js";
|
|
5
|
+
import { literalSchema, numberSchema, objectSchema, optionalSchema, stringSchema, } from "../schema.js";
|
|
4
6
|
import { buildDescriptorIndex, ensureDescriptor, extendSchemaWithOp, createOperationHandlers, invokeModuleTool, } from "./utils.js";
|
|
7
|
+
import { ToolError, toolErrorResult, unknownErrorResult, } from "../errors.js";
|
|
8
|
+
import { jsonResult } from "../responses.js";
|
|
5
9
|
const audioDescriptorIndex = buildDescriptorIndex(audioModule);
|
|
6
10
|
const metaDescriptorIndex = buildDescriptorIndex(metaModule);
|
|
11
|
+
const captureSamplesArgsSchema = objectSchema({
|
|
12
|
+
description: "Capture raw stereo PCM samples from the C64 Ultimate audio UDP stream.",
|
|
13
|
+
properties: {
|
|
14
|
+
op: literalSchema("capture_samples"),
|
|
15
|
+
count: optionalSchema(numberSchema({
|
|
16
|
+
description: "Number of stereo sample pairs to capture.",
|
|
17
|
+
integer: true,
|
|
18
|
+
minimum: 1,
|
|
19
|
+
maximum: 65536,
|
|
20
|
+
default: 256,
|
|
21
|
+
}), 256),
|
|
22
|
+
encoding: optionalSchema(stringSchema({
|
|
23
|
+
description: "Encoding used for the PCM sample payload.",
|
|
24
|
+
enum: ["base64", "hex"],
|
|
25
|
+
default: "base64",
|
|
26
|
+
}), "base64"),
|
|
27
|
+
},
|
|
28
|
+
required: ["op"],
|
|
29
|
+
additionalProperties: false,
|
|
30
|
+
});
|
|
7
31
|
const soundOperations = [
|
|
32
|
+
{
|
|
33
|
+
op: "capture_samples",
|
|
34
|
+
schema: captureSamplesArgsSchema.jsonSchema,
|
|
35
|
+
handler: async (rawArgs, ctx) => {
|
|
36
|
+
try {
|
|
37
|
+
const parsed = captureSamplesArgsSchema.parse(rawArgs);
|
|
38
|
+
ctx.logger.info("Capturing audio samples", {
|
|
39
|
+
count: parsed.count,
|
|
40
|
+
encoding: parsed.encoding,
|
|
41
|
+
});
|
|
42
|
+
const capture = await ctx.client.captureSamples({ count: parsed.count });
|
|
43
|
+
const samplesBuffer = Buffer.from(capture.samples.buffer, capture.samples.byteOffset, capture.samples.byteLength);
|
|
44
|
+
return jsonResult({
|
|
45
|
+
backend: capture.backend,
|
|
46
|
+
channels: capture.channels,
|
|
47
|
+
sampleRateHz: capture.sampleRateHz,
|
|
48
|
+
samplePairs: capture.samplePairs,
|
|
49
|
+
byteLength: samplesBuffer.length,
|
|
50
|
+
samples: {
|
|
51
|
+
encoding: parsed.encoding,
|
|
52
|
+
data: parsed.encoding === "hex"
|
|
53
|
+
? samplesBuffer.toString("hex")
|
|
54
|
+
: samplesBuffer.toString("base64"),
|
|
55
|
+
},
|
|
56
|
+
}, {
|
|
57
|
+
success: true,
|
|
58
|
+
backend: capture.backend,
|
|
59
|
+
samplePairs: capture.samplePairs,
|
|
60
|
+
sampleRateHz: capture.sampleRateHz,
|
|
61
|
+
channels: capture.channels,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (error instanceof ToolError) {
|
|
66
|
+
return toolErrorResult(error);
|
|
67
|
+
}
|
|
68
|
+
return unknownErrorResult(error);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
},
|
|
8
72
|
{
|
|
9
73
|
op: "set_volume",
|
|
10
74
|
schema: extendSchemaWithOp("set_volume", ensureDescriptor(audioDescriptorIndex, "sid_volume").inputSchema, { description: "Set the SID master volume register at $D418 (0-15)." }),
|
|
@@ -50,6 +114,11 @@ const soundOperations = [
|
|
|
50
114
|
schema: extendSchemaWithOp("compile_play", ensureDescriptor(audioDescriptorIndex, "music_compile_and_play").inputSchema, { description: "Compile SIDWAVE or CPG source and optionally play it immediately." }),
|
|
51
115
|
handler: async (rawArgs, ctx) => invokeModuleTool(audioModule, "music_compile_and_play", rawArgs, ctx),
|
|
52
116
|
},
|
|
117
|
+
{
|
|
118
|
+
op: "play_preset",
|
|
119
|
+
schema: extendSchemaWithOp("play_preset", ensureDescriptor(metaDescriptorIndex, "music_play_preset").inputSchema, { description: "Compile and play a built-in SID preset such as Für Elise by Beethoven." }),
|
|
120
|
+
handler: async (rawArgs, ctx) => invokeModuleTool(metaModule, "music_play_preset", rawArgs, ctx),
|
|
121
|
+
},
|
|
53
122
|
{
|
|
54
123
|
op: "pipeline",
|
|
55
124
|
schema: extendSchemaWithOp("pipeline", ensureDescriptor(metaDescriptorIndex, "music_compile_play_analyze").inputSchema, { description: "Compile a SIDWAVE score, play it, and analyze the recording." }),
|
|
@@ -70,6 +139,7 @@ const soundOperationHandlers = createOperationHandlers(soundOperations);
|
|
|
70
139
|
export const soundModuleGroup = defineToolModule({
|
|
71
140
|
domain: "audio",
|
|
72
141
|
summary: "Grouped SID control, playback, composition, and analysis operations.",
|
|
142
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
73
143
|
resources: ["c64://specs/sid", "c64://specs/sidwave", "c64://docs/sid/file-structure"],
|
|
74
144
|
prompts: ["sid-music"],
|
|
75
145
|
defaultTags: ["sid", "audio"],
|
|
@@ -87,12 +157,38 @@ export const soundModuleGroup = defineToolModule({
|
|
|
87
157
|
variants: soundOperations.map((operation) => operation.schema),
|
|
88
158
|
}),
|
|
89
159
|
tags: ["sid", "audio", "grouped"],
|
|
160
|
+
operationPlatforms: {
|
|
161
|
+
capture_samples: ["c64u"],
|
|
162
|
+
play_sid_file: ["c64u"],
|
|
163
|
+
play_mod_file: ["c64u"],
|
|
164
|
+
pipeline: ["c64u"],
|
|
165
|
+
analyze: ["c64u"],
|
|
166
|
+
record_analyze: ["c64u"],
|
|
167
|
+
},
|
|
168
|
+
operationToolNames: {
|
|
169
|
+
play_sid_file: "sidplay_file",
|
|
170
|
+
play_mod_file: "modplay_file",
|
|
171
|
+
play_preset: "music_play_preset",
|
|
172
|
+
pipeline: "music_compile_play_analyze",
|
|
173
|
+
analyze: "analyze_audio",
|
|
174
|
+
record_analyze: "record_and_analyze_audio",
|
|
175
|
+
},
|
|
90
176
|
examples: [
|
|
177
|
+
{
|
|
178
|
+
name: "Capture samples",
|
|
179
|
+
description: "Grab 256 stereo sample pairs from the Ultimate audio stream",
|
|
180
|
+
arguments: { op: "capture_samples" },
|
|
181
|
+
},
|
|
91
182
|
{
|
|
92
183
|
name: "Trigger SID voice",
|
|
93
184
|
description: "Start voice 1 on C4 with a triangle waveform",
|
|
94
185
|
arguments: { op: "note_on", voice: 1, note: "C4", waveform: "tri" },
|
|
95
186
|
},
|
|
187
|
+
{
|
|
188
|
+
name: "Play Für Elise",
|
|
189
|
+
description: "Compile and play the built-in Für Elise preset on the active backend",
|
|
190
|
+
arguments: { op: "play_preset", preset: "fuer_elise" },
|
|
191
|
+
},
|
|
96
192
|
{
|
|
97
193
|
name: "Verify silence",
|
|
98
194
|
description: "Stop all voices and confirm the SID output is quiet",
|
|
@@ -7,12 +7,12 @@ const metaDescriptorIndex = buildDescriptorIndex(metaModule);
|
|
|
7
7
|
const systemOperations = [
|
|
8
8
|
{
|
|
9
9
|
op: "pause",
|
|
10
|
-
schema: extendSchemaWithOp("pause", ensureDescriptor(machineDescriptorIndex, "pause").inputSchema, { description: "Pause the machine
|
|
10
|
+
schema: extendSchemaWithOp("pause", ensureDescriptor(machineDescriptorIndex, "pause").inputSchema, { description: "Pause the machine until resumed." }),
|
|
11
11
|
handler: async (rawArgs, ctx) => invokeModuleTool(machineControlModule, "pause", rawArgs, ctx),
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
op: "resume",
|
|
15
|
-
schema: extendSchemaWithOp("resume", ensureDescriptor(machineDescriptorIndex, "resume").inputSchema, { description: "Resume CPU execution after a
|
|
15
|
+
schema: extendSchemaWithOp("resume", ensureDescriptor(machineDescriptorIndex, "resume").inputSchema, { description: "Resume CPU execution after a pause." }),
|
|
16
16
|
handler: async (rawArgs, ctx) => invokeModuleTool(machineControlModule, "resume", rawArgs, ctx),
|
|
17
17
|
},
|
|
18
18
|
{
|
|
@@ -55,11 +55,17 @@ const systemOperations = [
|
|
|
55
55
|
schema: extendSchemaWithOp("list_tasks", ensureDescriptor(metaDescriptorIndex, "list_background_tasks").inputSchema, { description: "List known background tasks with status metadata." }),
|
|
56
56
|
handler: async (rawArgs, ctx) => invokeModuleTool(metaModule, "list_background_tasks", rawArgs, ctx),
|
|
57
57
|
},
|
|
58
|
+
{
|
|
59
|
+
op: "performance_report",
|
|
60
|
+
schema: extendSchemaWithOp("performance_report", ensureDescriptor(metaDescriptorIndex, "performance_report").inputSchema, { description: "Summarize diagnostics spans and tool latencies from the current or latest MCP session." }),
|
|
61
|
+
handler: async (rawArgs, ctx) => invokeModuleTool(metaModule, "performance_report", rawArgs, ctx),
|
|
62
|
+
},
|
|
58
63
|
];
|
|
59
64
|
const systemOperationHandlers = createOperationHandlers(systemOperations);
|
|
60
65
|
export const systemModuleGroup = defineToolModule({
|
|
61
66
|
domain: "system",
|
|
62
67
|
summary: "Grouped machine control and background task orchestration.",
|
|
68
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
63
69
|
resources: ["c64://context/bootstrap"],
|
|
64
70
|
prompts: ["memory-debug"],
|
|
65
71
|
defaultTags: ["system", "control"],
|
|
@@ -77,6 +83,8 @@ export const systemModuleGroup = defineToolModule({
|
|
|
77
83
|
variants: systemOperations.map((operation) => operation.schema),
|
|
78
84
|
}),
|
|
79
85
|
tags: ["system", "control", "grouped"],
|
|
86
|
+
operationPlatforms: { pause: ["c64u"], resume: ["c64u"], menu: ["c64u"] },
|
|
87
|
+
operationToolNames: { pause: "pause", resume: "resume", menu: "menu_button" },
|
|
80
88
|
examples: [
|
|
81
89
|
{
|
|
82
90
|
name: "Soft reset",
|
|
@@ -93,6 +101,11 @@ export const systemModuleGroup = defineToolModule({
|
|
|
93
101
|
intervalMs: 2000,
|
|
94
102
|
},
|
|
95
103
|
},
|
|
104
|
+
{
|
|
105
|
+
name: "Inspect performance trace",
|
|
106
|
+
description: "Summarize the current session diagnostics for profiling",
|
|
107
|
+
arguments: { op: "performance_report", scope: "current" },
|
|
108
|
+
},
|
|
96
109
|
],
|
|
97
110
|
execute: createOperationDispatcher("c64_system", systemOperationHandlers),
|
|
98
111
|
},
|
package/dist/tools/storage.js
CHANGED
|
@@ -143,6 +143,7 @@ const createDnpArgsSchema = objectSchema({
|
|
|
143
143
|
export const storageModule = defineToolModule({
|
|
144
144
|
domain: "storage",
|
|
145
145
|
summary: "Drive management, disk image creation, and file inspection utilities.",
|
|
146
|
+
supportedPlatforms: ["c64u", "vice"],
|
|
146
147
|
resources: ["c64://context/bootstrap"],
|
|
147
148
|
prompts: ["memory-debug"],
|
|
148
149
|
defaultTags: ["drive", "storage"],
|
package/dist/tools/streaming.js
CHANGED
|
@@ -42,6 +42,7 @@ export const streamingModule = defineToolModule({
|
|
|
42
42
|
prompts: ["sid-music"],
|
|
43
43
|
defaultLifecycle: "stream",
|
|
44
44
|
defaultTags: ["stream", "monitoring"],
|
|
45
|
+
supportedPlatforms: ["c64u"],
|
|
45
46
|
workflowHints: [
|
|
46
47
|
"Use streaming tools for long-running capture or monitoring workflows such as audio verification.",
|
|
47
48
|
"Clarify that streams keep running until stopped so the user can manage resources.",
|