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,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import "./bootstrap/stdio-logger.js";
2
3
  import { dirname, join } from "node:path";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -7,16 +8,29 @@ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSche
7
8
  import { loadConfig } from "./config.js";
8
9
  import { C64Client } from "./c64Client.js";
9
10
  import { listKnowledgeResources, readKnowledgeResource, } from "./rag/knowledgeIndex.js";
10
- import { initRag } from "./rag/init.js";
11
+ import { createLazyRagRetriever, initRag } from "./rag/init.js";
12
+ import { getMcpServerImplementationInfo } from "./mcp/metadata.js";
11
13
  import { toolRegistry } from "./tools/registry/index.js";
12
14
  import { unknownErrorResult } from "./tools/errors.js";
13
15
  import { createPromptRegistry } from "./prompts/registry.js";
14
16
  import { describePlatformCapabilities, getPlatformStatus, setPlatform } from "./platform.js";
15
17
  import axios from "axios";
16
18
  import { loggerFor, payloadByteLength, formatPayloadForDebug, formatErrorMessage } from "./logger.js";
19
+ import { getDiagnosticsSessionInfo, installProcessDiagnostics, withDiagnosticSpan, writeDiagnosticEvent } from "./diagnostics.js";
17
20
  const __filename = fileURLToPath(import.meta.url);
18
21
  const __dirname = dirname(__filename);
19
22
  const PROJECT_ROOT = join(__dirname, "..");
23
+ function createPromptRegistryGetter() {
24
+ let registry;
25
+ return () => {
26
+ if (!registry) {
27
+ writeDiagnosticEvent("prompt_registry_init_start");
28
+ registry = createPromptRegistry();
29
+ writeDiagnosticEvent("prompt_registry_init_complete");
30
+ }
31
+ return registry;
32
+ };
33
+ }
20
34
  function parseCliOptions(argv) {
21
35
  const httpIndex = argv.indexOf("--http");
22
36
  if (httpIndex === -1) {
@@ -36,21 +50,51 @@ function parsePort(raw) {
36
50
  return undefined;
37
51
  }
38
52
  async function main() {
53
+ const diagnostics = installProcessDiagnostics("mcp-server");
39
54
  console.error("Starting c64bridge MCP server...");
40
- const config = loadConfig();
55
+ console.error(`c64bridge diagnostics file: ${diagnostics.filePath}`);
56
+ writeDiagnosticEvent("server_start", { diagnosticsFile: diagnostics.filePath });
57
+ const config = await withDiagnosticSpan("startup", "load_config", {}, () => Promise.resolve(loadConfig()));
41
58
  const baseUrl = config.baseUrl ?? `http://${config.c64_host}`;
59
+ writeDiagnosticEvent("config_loaded", {
60
+ baseUrl,
61
+ hasNetworkPassword: Boolean(config.networkPassword),
62
+ });
42
63
  // Initialize C64 client (reuse existing)
43
- const client = new C64Client(baseUrl, { networkPassword: config.networkPassword });
44
- const rag = await initRag();
64
+ const client = await withDiagnosticSpan("startup", "create_client", { baseUrl }, async () => new C64Client(baseUrl, {
65
+ networkPassword: config.networkPassword,
66
+ forceC64uFacade: false,
67
+ }));
68
+ const initialBackendType = await withDiagnosticSpan("startup", "resolve_active_backend", {}, () => client.getActiveBackendType());
69
+ setPlatform(initialBackendType);
70
+ writeDiagnosticEvent("platform_initialised", { platform: initialBackendType });
71
+ void withDiagnosticSpan("startup", "prewarm_backends", { backends: ["vice"] }, () => client.prewarmBackends(["vice"])).then((results) => {
72
+ writeDiagnosticEvent("backend_prewarm_complete", { results });
73
+ }).catch((error) => {
74
+ writeDiagnosticEvent("backend_prewarm_failed", { error });
75
+ });
76
+ const rag = createLazyRagRetriever(() => initRag(), {
77
+ onInitStart() {
78
+ writeDiagnosticEvent("rag_init_start");
79
+ },
80
+ onInitComplete() {
81
+ writeDiagnosticEvent("rag_init_complete");
82
+ },
83
+ onInitError(error) {
84
+ writeDiagnosticEvent("rag_init_failed", { error });
85
+ },
86
+ });
87
+ void rag.warmup?.().catch(() => {
88
+ // Diagnostics already record the failure; keep startup non-blocking.
89
+ });
45
90
  const toolLogger = loggerFor("tool");
46
91
  const resourceLogger = loggerFor("resource");
47
92
  const promptLogger = loggerFor("prompt");
48
93
  const promptRegistry = createPromptRegistry();
94
+ const getPromptRegistry = createPromptRegistryGetter();
95
+ const implementationInfo = getMcpServerImplementationInfo();
49
96
  // Create MCP server
50
- const server = new Server({
51
- name: "c64bridge",
52
- version: "0.4.0",
53
- }, {
97
+ const server = new Server(implementationInfo, {
54
98
  capabilities: {
55
99
  resources: {},
56
100
  tools: {},
@@ -60,13 +104,14 @@ async function main() {
60
104
  // RESOURCES: Expose C64 knowledge base
61
105
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
62
106
  const startedAt = Date.now();
107
+ writeDiagnosticEvent("mcp_list_resources_start");
63
108
  try {
64
109
  const knowledgeResources = listKnowledgeResources().map((resource) => ({
65
110
  uri: resource.uri,
66
111
  name: resource.name,
67
112
  description: resource.description,
68
113
  mimeType: resource.mimeType,
69
- metadata: resource.metadata,
114
+ _meta: resource.metadata,
70
115
  }));
71
116
  const platformResource = createPlatformResourceDescriptor();
72
117
  const response = {
@@ -79,6 +124,10 @@ async function main() {
79
124
  resourceLogger.debug("list resources request", { request: {} });
80
125
  resourceLogger.debug("list resources response", { response: formatPayloadForDebug(response) });
81
126
  }
127
+ writeDiagnosticEvent("mcp_list_resources_ok", {
128
+ count: response.resources.length,
129
+ latencyMs: latency,
130
+ });
82
131
  return response;
83
132
  }
84
133
  catch (error) {
@@ -88,11 +137,13 @@ async function main() {
88
137
  resourceLogger.debug("list resources request", { request: {} });
89
138
  resourceLogger.debug("list resources error", { error: formatErrorMessage(error) });
90
139
  }
140
+ writeDiagnosticEvent("mcp_list_resources_failed", { latencyMs: latency, error });
91
141
  throw error;
92
142
  }
93
143
  });
94
144
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
95
145
  const startedAt = Date.now();
146
+ writeDiagnosticEvent("mcp_read_resource_start", { uri: request.params.uri });
96
147
  try {
97
148
  let response;
98
149
  if (request.params.uri === PLATFORM_RESOURCE_URI) {
@@ -101,7 +152,7 @@ async function main() {
101
152
  {
102
153
  uri: PLATFORM_RESOURCE_URI,
103
154
  mimeType: "text/markdown",
104
- text: renderPlatformStatusMarkdown(),
155
+ text: renderPlatformStatusMarkdown(client),
105
156
  },
106
157
  ],
107
158
  };
@@ -122,6 +173,10 @@ async function main() {
122
173
  resourceLogger.debug("read resource request", { request: formatPayloadForDebug(request.params) });
123
174
  resourceLogger.debug("read resource response", { response: formatPayloadForDebug(response) });
124
175
  }
176
+ writeDiagnosticEvent("mcp_read_resource_ok", {
177
+ uri: request.params.uri,
178
+ latencyMs: latency,
179
+ });
125
180
  return response;
126
181
  }
127
182
  catch (error) {
@@ -131,14 +186,26 @@ async function main() {
131
186
  resourceLogger.debug("read resource request", { request: formatPayloadForDebug(request.params) });
132
187
  resourceLogger.debug("read resource error", { error: formatErrorMessage(error) });
133
188
  }
189
+ writeDiagnosticEvent("mcp_read_resource_failed", {
190
+ uri: request.params.uri,
191
+ latencyMs: latency,
192
+ error,
193
+ });
134
194
  throw error;
135
195
  }
136
196
  });
137
197
  server.setRequestHandler(ListToolsRequestSchema, async () => {
138
198
  const startedAt = Date.now();
199
+ writeDiagnosticEvent("mcp_list_tools_start");
139
200
  try {
140
201
  const response = {
141
- tools: toolRegistry.list(),
202
+ tools: toolRegistry.list().map((tool) => ({
203
+ name: tool.name,
204
+ title: tool.metadata.summary,
205
+ description: tool.description,
206
+ inputSchema: tool.inputSchema,
207
+ _meta: tool.metadata,
208
+ })),
142
209
  };
143
210
  const latency = Date.now() - startedAt;
144
211
  const bytes = payloadByteLength(response);
@@ -147,6 +214,10 @@ async function main() {
147
214
  toolLogger.debug("list tools request", { request: {} });
148
215
  toolLogger.debug("list tools response", { response: formatPayloadForDebug(response) });
149
216
  }
217
+ writeDiagnosticEvent("mcp_list_tools_ok", {
218
+ count: response.tools.length,
219
+ latencyMs: latency,
220
+ });
150
221
  return response;
151
222
  }
152
223
  catch (error) {
@@ -156,11 +227,13 @@ async function main() {
156
227
  toolLogger.debug("list tools request", { request: {} });
157
228
  toolLogger.debug("list tools error", { error: formatErrorMessage(error) });
158
229
  }
230
+ writeDiagnosticEvent("mcp_list_tools_failed", { latencyMs: latency, error });
159
231
  throw error;
160
232
  }
161
233
  });
162
234
  server.setRequestHandler(ListPromptsRequestSchema, async () => {
163
235
  const startedAt = Date.now();
236
+ writeDiagnosticEvent("mcp_list_prompts_start");
164
237
  try {
165
238
  const entries = promptRegistry.list();
166
239
  const response = {
@@ -179,6 +252,9 @@ async function main() {
179
252
  optionalResources: entry.descriptor.optionalResources ?? [],
180
253
  tools: entry.descriptor.tools,
181
254
  tags: entry.descriptor.tags ?? [],
255
+ argumentOptions: Object.fromEntries((entry.arguments ?? [])
256
+ .filter((argument) => Array.isArray(argument.options) && argument.options.length > 0)
257
+ .map((argument) => [argument.name, argument.options])),
182
258
  },
183
259
  })),
184
260
  };
@@ -189,6 +265,10 @@ async function main() {
189
265
  promptLogger.debug("list prompts request", { request: {} });
190
266
  promptLogger.debug("list prompts response", { response: formatPayloadForDebug(response) });
191
267
  }
268
+ writeDiagnosticEvent("mcp_list_prompts_ok", {
269
+ count: response.prompts.length,
270
+ latencyMs: latency,
271
+ });
192
272
  return response;
193
273
  }
194
274
  catch (error) {
@@ -198,6 +278,7 @@ async function main() {
198
278
  promptLogger.debug("list prompts request", { request: {} });
199
279
  promptLogger.debug("list prompts error", { error: formatErrorMessage(error) });
200
280
  }
281
+ writeDiagnosticEvent("mcp_list_prompts_failed", { latencyMs: latency, error });
201
282
  throw error;
202
283
  }
203
284
  });
@@ -205,6 +286,10 @@ async function main() {
205
286
  const { name } = request.params;
206
287
  const args = request.params.arguments ?? {};
207
288
  const startedAt = Date.now();
289
+ writeDiagnosticEvent("mcp_call_tool_start", {
290
+ name,
291
+ arguments: formatPayloadForDebug(args),
292
+ });
208
293
  if (toolLogger.isDebugEnabled()) {
209
294
  toolLogger.debug("tool request", {
210
295
  name,
@@ -230,6 +315,11 @@ async function main() {
230
315
  response: formatPayloadForDebug(response),
231
316
  });
232
317
  }
318
+ writeDiagnosticEvent("mcp_call_tool_ok", {
319
+ name,
320
+ latencyMs: latency,
321
+ isError: result.isError === true,
322
+ });
233
323
  return response;
234
324
  }
235
325
  catch (error) {
@@ -245,6 +335,11 @@ async function main() {
245
335
  error: formatErrorMessage(error),
246
336
  });
247
337
  }
338
+ writeDiagnosticEvent("mcp_call_tool_failed", {
339
+ name,
340
+ latencyMs: latency,
341
+ error,
342
+ });
248
343
  return response;
249
344
  }
250
345
  });
@@ -252,6 +347,10 @@ async function main() {
252
347
  const { name } = request.params;
253
348
  const args = request.params.arguments ?? {};
254
349
  const startedAt = Date.now();
350
+ writeDiagnosticEvent("mcp_get_prompt_start", {
351
+ name,
352
+ arguments: formatPayloadForDebug(args),
353
+ });
255
354
  if (promptLogger.isDebugEnabled()) {
256
355
  promptLogger.debug("prompt request", {
257
356
  name,
@@ -284,6 +383,10 @@ async function main() {
284
383
  response: formatPayloadForDebug(response),
285
384
  });
286
385
  }
386
+ writeDiagnosticEvent("mcp_get_prompt_ok", {
387
+ name,
388
+ latencyMs: latency,
389
+ });
287
390
  return response;
288
391
  }
289
392
  catch (error) {
@@ -296,14 +399,23 @@ async function main() {
296
399
  });
297
400
  promptLogger.debug("prompt error", { name, error: formatErrorMessage(error) });
298
401
  }
402
+ writeDiagnosticEvent("mcp_get_prompt_failed", {
403
+ name,
404
+ latencyMs: latency,
405
+ error,
406
+ });
299
407
  throw error;
300
408
  }
301
409
  });
302
410
  // Connect via stdio
303
411
  const transport = new StdioServerTransport();
304
412
  await server.connect(transport);
413
+ writeDiagnosticEvent("mcp_transport_connected", { mode: "stdio" });
305
414
  await logConnectivity(client, baseUrl);
306
415
  console.error("c64bridge MCP server running on stdio");
416
+ writeDiagnosticEvent("server_ready", {
417
+ diagnosticsFile: diagnostics.filePath,
418
+ });
307
419
  }
308
420
  const PLATFORM_RESOURCE_URI = "c64://platform/status";
309
421
  function createPlatformResourceDescriptor() {
@@ -312,7 +424,7 @@ function createPlatformResourceDescriptor() {
312
424
  name: "Active Platform Status",
313
425
  description: "Reports the active MCP platform and tool compatibility snapshot.",
314
426
  mimeType: "text/markdown",
315
- metadata: {
427
+ _meta: {
316
428
  domain: "platform",
317
429
  priority: "critical",
318
430
  summary: "Current platform (c64u or vice), feature flags, and tool support overview.",
@@ -323,14 +435,19 @@ function createPlatformResourceDescriptor() {
323
435
  },
324
436
  };
325
437
  }
326
- function renderPlatformStatusMarkdown() {
438
+ function renderPlatformStatusMarkdown(client) {
327
439
  const status = getPlatformStatus();
440
+ const availableBackends = client.getAvailableBackends();
328
441
  const capabilities = describePlatformCapabilities(toolRegistry.list());
329
442
  const lines = [
330
443
  "# MCP Platform Status",
331
444
  "",
332
445
  `Current platform: \`${status.id}\``,
333
446
  "",
447
+ "## Available Backends",
448
+ "",
449
+ ...availableBackends.map((backend) => `- \`${backend}\`${backend === status.id ? " (active)" : ""}`),
450
+ "",
334
451
  status.features.length > 0
335
452
  ? ["## Active Features", "", ...status.features.map((feature) => `- ${feature}`)].join("\n")
336
453
  : "",
@@ -353,10 +470,33 @@ function renderPlatformStatusMarkdown() {
353
470
  : "- Unsupported tools: _none_");
354
471
  lines.push("");
355
472
  }
356
- lines.push("> Switching platforms currently requires restarting the MCP server with an updated configuration.");
473
+ lines.push("> Use `c64_select_backend` to switch to another available backend without restarting the MCP server.");
357
474
  return lines.join("\n");
358
475
  }
359
476
  async function logConnectivity(client, baseUrl) {
477
+ const backendType = await client.getBackendType();
478
+ if (backendType !== "c64u") {
479
+ writeDiagnosticEvent("connectivity_probe_skipped", { baseUrl, reason: "vice_backend" });
480
+ console.error("Skipping direct REST connectivity probe because active backend is VICE");
481
+ try {
482
+ const memoryAddress = "$0000";
483
+ const memoryResult = await client.readMemory(memoryAddress, "1");
484
+ if (memoryResult.success && memoryResult.data) {
485
+ writeDiagnosticEvent("zero_page_probe_ok", { address: memoryAddress, data: memoryResult.data, backendType });
486
+ console.error(`VICE zero-page probe @ ${memoryAddress}: ${memoryResult.data}`);
487
+ }
488
+ else if (memoryResult.details) {
489
+ writeDiagnosticEvent("zero_page_probe_failed", { address: memoryAddress, details: memoryResult.details, backendType });
490
+ console.error(`VICE zero-page probe failed: ${JSON.stringify(memoryResult.details)}`);
491
+ }
492
+ }
493
+ catch (memoryError) {
494
+ const message = memoryError instanceof Error ? memoryError.message : String(memoryError);
495
+ writeDiagnosticEvent("zero_page_probe_failed", { address: "$0000", error: memoryError, backendType });
496
+ console.error(`VICE zero-page probe skipped or failed: ${message}`);
497
+ }
498
+ return;
499
+ }
360
500
  const c64Logger = loggerFor("c64u");
361
501
  const startedAt = Date.now();
362
502
  const infoUrl = new URL("/v1/info", baseUrl).toString();
@@ -378,27 +518,33 @@ async function logConnectivity(client, baseUrl) {
378
518
  else {
379
519
  c64Logger.error(`GET ${infoUrl} status=ERR bytes=0 latencyMs=${latency} error=${formatErrorMessage(error)}`);
380
520
  }
381
- console.log(`Skipping direct REST connectivity probe (no hardware REST base reachable at ${baseUrl})`);
521
+ writeDiagnosticEvent("connectivity_probe_skipped", { baseUrl, latencyMs: latency, error });
522
+ console.error(`Skipping direct REST connectivity probe (no hardware REST base reachable at ${baseUrl})`);
382
523
  return;
383
524
  }
384
525
  if (!response) {
385
- console.log(`Skipping direct REST connectivity probe (no hardware REST base reachable at ${baseUrl})`);
526
+ writeDiagnosticEvent("connectivity_probe_skipped", { baseUrl, reason: "empty_response" });
527
+ console.error(`Skipping direct REST connectivity probe (no hardware REST base reachable at ${baseUrl})`);
386
528
  return;
387
529
  }
388
- console.log(`Connectivity check succeeded for c64 device at ${baseUrl}`);
530
+ writeDiagnosticEvent("connectivity_probe_ok", { baseUrl, status: response.status });
531
+ console.error(`Connectivity check succeeded for c64 device at ${baseUrl}`);
389
532
  try {
390
533
  const memoryAddress = "$0000";
391
534
  const memoryResult = await client.readMemory(memoryAddress, "1");
392
535
  if (memoryResult.success && memoryResult.data) {
393
- console.log(`Zero-page probe @ ${memoryAddress}: ${memoryResult.data}`);
536
+ writeDiagnosticEvent("zero_page_probe_ok", { address: memoryAddress, data: memoryResult.data });
537
+ console.error(`Zero-page probe @ ${memoryAddress}: ${memoryResult.data}`);
394
538
  }
395
539
  else if (memoryResult.details) {
396
- console.warn(`Zero-page probe failed: ${JSON.stringify(memoryResult.details)}`);
540
+ writeDiagnosticEvent("zero_page_probe_failed", { address: memoryAddress, details: memoryResult.details });
541
+ console.error(`Zero-page probe failed: ${JSON.stringify(memoryResult.details)}`);
397
542
  }
398
543
  }
399
544
  catch (memoryError) {
400
545
  const message = memoryError instanceof Error ? memoryError.message : String(memoryError);
401
- console.warn(`Zero-page probe skipped or failed (may be unsupported on current backend): ${message}`);
546
+ writeDiagnosticEvent("zero_page_probe_failed", { address: "$0000", error: memoryError });
547
+ console.error(`Zero-page probe skipped or failed (may be unsupported on current backend): ${message}`);
402
548
  }
403
549
  }
404
550
  function toCallToolResult(result) {
@@ -425,6 +571,10 @@ function toPromptMessage(segment) {
425
571
  };
426
572
  }
427
573
  main().catch((error) => {
574
+ writeDiagnosticEvent("server_fatal", {
575
+ diagnosticsFile: getDiagnosticsSessionInfo()?.filePath,
576
+ error,
577
+ });
428
578
  console.error("Fatal error in MCP server:", error);
429
579
  process.exit(1);
430
580
  });
package/dist/platform.js CHANGED
@@ -11,8 +11,16 @@ const PLATFORM_FEATURES = {
11
11
  limited: [],
12
12
  },
13
13
  vice: {
14
- features: ["software-emulation", "prg-autostart"],
15
- limited: ["no-rest-api", "no-drive-management", "limited-sid"],
14
+ features: [
15
+ "software-emulation",
16
+ "binary-monitor",
17
+ "memory-io",
18
+ "screen-capture",
19
+ "system-control",
20
+ "drive-management",
21
+ "runtime-config",
22
+ ],
23
+ limited: ["no-rest-api", "no-firmware-filesystem", "no-flash-config", "limited-sid"],
16
24
  },
17
25
  };
18
26
  let currentPlatform = process.env.C64_MODE === "vice" ? "vice" : "c64u";