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.
Files changed (109) hide show
  1. package/.github/prompts/README.md +14 -4
  2. package/.github/prompts/assembly-routine-session.prompt.md +9 -9
  3. package/.github/prompts/basic-program-session.prompt.md +11 -9
  4. package/.github/prompts/drive-management-session.prompt.md +9 -9
  5. package/.github/prompts/hello-world-session.prompt.md +17 -0
  6. package/.github/prompts/petscii-art-session.prompt.md +9 -9
  7. package/.github/prompts/play-classic-game.prompt.md +9 -9
  8. package/.github/prompts/pr-converge.prompt.md +13 -0
  9. package/.github/prompts/sid-music-session.prompt.md +9 -9
  10. package/AGENTS.md +59 -60
  11. package/README.md +550 -191
  12. package/data/assembly/assembly-spec.md +290 -1
  13. package/data/context/bootstrap.md +26 -2
  14. package/data/context/fast-paths.md +44 -0
  15. package/data/graphics/petscii-style-guide.md +6 -2
  16. package/data/graphics/sprite-charset-best-practices.md +5 -5
  17. package/dist/.tsbuildinfo +1 -1
  18. package/dist/c64Client.js +990 -26
  19. package/dist/config.js +5 -1
  20. package/dist/device.js +680 -57
  21. package/dist/diagnostics.js +371 -0
  22. package/dist/mcp/metadata.js +39 -0
  23. package/dist/mcp-server.js +170 -20
  24. package/dist/platform.js +10 -2
  25. package/dist/prompts/registry.js +166 -309
  26. package/dist/rag/init.js +37 -1
  27. package/dist/rag/knowledgeIndex.js +18 -6
  28. package/dist/rag/rateLimiter.js +2 -2
  29. package/dist/streamCapture.js +135 -0
  30. package/dist/tools/audio.js +3 -1
  31. package/dist/tools/debug.js +470 -0
  32. package/dist/tools/developer.js +10 -4
  33. package/dist/tools/graphics.js +183 -17
  34. package/dist/tools/machineControl.js +11 -5
  35. package/dist/tools/memory.js +19 -10
  36. package/dist/tools/meta/audio.js +240 -1
  37. package/dist/tools/meta/config.js +20 -28
  38. package/dist/tools/meta/configInventory.js +104 -0
  39. package/dist/tools/meta/diagnostics.js +45 -2
  40. package/dist/tools/meta/graphics.js +14 -6
  41. package/dist/tools/meta/index.js +1 -0
  42. package/dist/tools/meta/memory.js +20 -6
  43. package/dist/tools/meta/program.js +473 -0
  44. package/dist/tools/pollValidator.js +4 -2
  45. package/dist/tools/programRunners.js +8 -4
  46. package/dist/tools/registry/config.js +17 -0
  47. package/dist/tools/registry/disk.js +7 -0
  48. package/dist/tools/registry/drive.js +3 -0
  49. package/dist/tools/registry/graphics.js +111 -27
  50. package/dist/tools/registry/index.js +6 -0
  51. package/dist/tools/registry/memory.js +1 -0
  52. package/dist/tools/registry/platform.js +121 -0
  53. package/dist/tools/registry/program.js +26 -8
  54. package/dist/tools/registry/sound.js +96 -0
  55. package/dist/tools/registry/system.js +15 -2
  56. package/dist/tools/storage.js +1 -0
  57. package/dist/tools/streaming.js +1 -0
  58. package/dist/tools/types.js +38 -4
  59. package/dist/tools/vice.js +160 -0
  60. package/dist/vicBitmap.js +317 -0
  61. package/dist/vice/mockServer.js +439 -0
  62. package/dist/vice/process.js +310 -0
  63. package/dist/vice/readiness.js +18 -2
  64. package/dist/vice/viceClient.js +382 -22
  65. package/doc/developer.md +71 -25
  66. package/doc/img/backend-switch/hello-c64u.png +0 -0
  67. package/doc/img/backend-switch/hello-vice-and-c64u.png +0 -0
  68. package/doc/img/backend-switch/hello-vice.png +0 -0
  69. package/doc/img/backend-switch/metadata.json +50 -0
  70. package/doc/plans/vice-backend-switch/plan.md +486 -0
  71. package/doc/plans/vice-backend-switch-2/plan.md +48 -0
  72. package/doc/plans/vice-support/notes.md +1 -0
  73. package/doc/plans/vice-support/plan.md +5 -0
  74. package/doc/plans/vice-support/rollout.md +23 -17
  75. package/doc/reviews/vice-support-review.md +185 -0
  76. package/doc/troubleshooting-mcp.md +35 -6
  77. package/doc/vice/support-matrix.md +54 -0
  78. package/mcp/prompts.json +292 -0
  79. package/mcp/protocol-examples.json +46 -0
  80. package/mcp/resources.json +940 -0
  81. package/mcp/schemas/c64_config.schema.json +350 -0
  82. package/mcp/schemas/c64_debug.schema.json +380 -0
  83. package/mcp/schemas/c64_disk.schema.json +278 -0
  84. package/mcp/schemas/c64_drive.schema.json +120 -0
  85. package/mcp/schemas/c64_extract.schema.json +302 -0
  86. package/mcp/schemas/c64_graphics.schema.json +333 -0
  87. package/mcp/schemas/c64_memory.schema.json +155 -0
  88. package/mcp/schemas/c64_printer.schema.json +218 -0
  89. package/mcp/schemas/c64_program.schema.json +391 -0
  90. package/mcp/schemas/c64_rag.schema.json +67 -0
  91. package/mcp/schemas/c64_select_backend.schema.json +32 -0
  92. package/mcp/schemas/c64_sound.schema.json +644 -0
  93. package/mcp/schemas/c64_stream.schema.json +61 -0
  94. package/mcp/schemas/c64_system.schema.json +227 -0
  95. package/mcp/schemas/c64_vice.schema.json +63 -0
  96. package/mcp/server.json +15 -0
  97. package/mcp/tools.json +4517 -0
  98. package/mcp.json +90 -15
  99. package/package.json +11 -4
  100. package/scripts/capture-backend-switch-screenshots.mjs +276 -0
  101. package/scripts/generate-mcp-interface.ts +289 -0
  102. package/scripts/invoke-bun.mjs +67 -21
  103. package/scripts/mockC64Server.mjs +162 -6
  104. package/scripts/prepare-release.mjs +59 -2
  105. package/scripts/run-coverage.mjs +284 -0
  106. package/scripts/run-tests.mjs +95 -39
  107. package/scripts/run-tests.ts +378 -73
  108. package/scripts/update-readme.ts +147 -13
  109. 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 { ToolExecutionError, toolErrorResult } from "../errors.js";
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: "create_petscii",
9
- schema: extendSchemaWithOp("create_petscii", ensureDescriptor(graphicsDescriptorIndex, "create_petscii").inputSchema, { description: "Generate PETSCII art from prompts, text, or explicit bitmap data." }),
10
- handler: groupedGraphicsHandlers.create_petscii,
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: "render_petscii",
14
- schema: extendSchemaWithOp("render_petscii", ensureDescriptor(graphicsDescriptorIndex, "render_petscii").inputSchema, { description: "Render PETSCII text with optional border/background colours." }),
15
- handler: groupedGraphicsHandlers.render_petscii,
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: "generate_sprite",
19
- schema: extendSchemaWithOp("generate_sprite", ensureDescriptor(graphicsDescriptorIndex, "generate_sprite").inputSchema, { description: "Build and run a sprite PRG from raw 63-byte sprite data." }),
20
- handler: groupedGraphicsHandlers.generate_sprite,
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: "generate_bitmap",
24
- schema: extendSchemaWithOp("generate_bitmap", {
25
- description: "Reserved high-resolution bitmap generator (coming soon).",
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 PETSCII, sprite, and upcoming bitmap helpers.",
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 PETSCII helpers for text art and clarify whether the BASIC program executed or stayed a dry run.",
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 PETSCII art, sprite previews, and future bitmap generation.",
51
- summary: "Generates PETSCII art, renders text screens, or runs sprite demos from one tool.",
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: "create_petscii", prompt: "duck on a pond", dryRun: true },
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: "render_petscii", text: "HELLO", borderColor: 6 },
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: "generate_sprite", sprite: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" },
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, programOperationHandlers as groupedProgramHandlers } from "../programRunners.js";
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: groupedProgramHandlers.load_prg,
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 located on the Ultimate filesystem." }),
16
- handler: groupedProgramHandlers.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." }),
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: groupedProgramHandlers.run_crt,
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: groupedProgramHandlers.upload_run_basic,
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: groupedProgramHandlers.upload_run_asm,
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 an Ultimate filesystem path instead of source text.",
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 using DMA halt until resumed." }),
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 DMA pause." }),
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
  },
@@ -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"],
@@ -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.",