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
package/dist/mcp-server.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(">
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: [
|
|
15
|
-
|
|
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";
|