@ray0404/zig-audio-mcp 0.1.4 → 0.2.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/.kilo/plans/mcp-resources-plan.md +174 -0
- package/AGENTS.md +53 -5
- package/GEMINI.md +65 -4
- package/README.md +82 -6
- package/dist/index.js +1037 -12
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +1093 -12
- package/src/test.ts +259 -6
- package/tsconfig.tsbuildinfo +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,100 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
// Custom error types for structured error handling
|
|
7
|
+
class McpResourceError extends Error {
|
|
8
|
+
constructor(uri) {
|
|
9
|
+
super(`Resource not found: ${uri}`);
|
|
10
|
+
this.name = "McpResourceError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
class McpToolError extends Error {
|
|
14
|
+
constructor(toolName, message) {
|
|
15
|
+
super(`Tool '${toolName}': ${message}`);
|
|
16
|
+
this.name = "McpToolError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
class McpValidationError extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(`Validation error: ${message}`);
|
|
22
|
+
this.name = "McpValidationError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Structured logging utility
|
|
26
|
+
const log = {
|
|
27
|
+
info: (message, data) => {
|
|
28
|
+
console.error(`[INFO] ${message}`, data ? JSON.stringify(data) : "");
|
|
29
|
+
},
|
|
30
|
+
warn: (message, data) => {
|
|
31
|
+
console.error(`[WARN] ${message}`, data ? JSON.stringify(data) : "");
|
|
32
|
+
},
|
|
33
|
+
error: (message, error) => {
|
|
34
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
35
|
+
console.error(`[ERROR] ${message}: ${errorMsg}`);
|
|
36
|
+
},
|
|
37
|
+
debug: (message, data) => {
|
|
38
|
+
console.error(`[DEBUG] ${message}`, data ? JSON.stringify(data) : "");
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
// MCP Prompts data
|
|
42
|
+
const PROMPTS = {
|
|
43
|
+
"library-selection": {
|
|
44
|
+
name: "library-selection",
|
|
45
|
+
description: "Help select the right audio library for your Zig project",
|
|
46
|
+
arguments: [
|
|
47
|
+
{ name: "project_type", description: "synthesizer, audio-player, dsp-effects, file-io, sdr-radio, game-audio, embedded", required: true },
|
|
48
|
+
{ name: "requirements", description: "optional: low-latency, no-allocations, cross-platform, simple-api", required: false }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"synth-basics": {
|
|
52
|
+
name: "synth-basics",
|
|
53
|
+
description: "Step-by-step guide to building a basic synthesizer in Zig",
|
|
54
|
+
arguments: [
|
|
55
|
+
{ name: "output_type", description: "wav-file, playback, both", required: false }
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"filter-design": {
|
|
59
|
+
name: "filter-design",
|
|
60
|
+
description: "Guide to designing audio filters in Zig",
|
|
61
|
+
arguments: [
|
|
62
|
+
{ name: "filter_type", description: "lowpass, highpass, bandpass, notch, peaking", required: true },
|
|
63
|
+
{ name: "sample_rate", description: "44100, 48000, 96000", required: false }
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
"debugging-audio": {
|
|
67
|
+
name: "debugging-audio",
|
|
68
|
+
description: "Troubleshoot common audio programming issues",
|
|
69
|
+
arguments: [
|
|
70
|
+
{ name: "issue", description: "glitches, latency, no-sound, crash, poor-quality", required: true }
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
function generatePromptContent(promptName, args) {
|
|
75
|
+
switch (promptName) {
|
|
76
|
+
case "library-selection": {
|
|
77
|
+
const projectType = args.project_type || "synthesizer";
|
|
78
|
+
const requirements = args.requirements || "";
|
|
79
|
+
return `Help select the best Zig audio library for a ${projectType} project${requirements ? ` with requirements: ${requirements}` : ""}.`;
|
|
80
|
+
}
|
|
81
|
+
case "synth-basics": {
|
|
82
|
+
const outputType = args.output_type || "playback";
|
|
83
|
+
return `Provide a step-by-step guide to building a basic synthesizer in Zig that outputs to ${outputType}. Include oscillator setup, envelope, and audio output.`;
|
|
84
|
+
}
|
|
85
|
+
case "filter-design": {
|
|
86
|
+
const filterType = args.filter_type || "lowpass";
|
|
87
|
+
const sampleRate = args.sample_rate || "44100";
|
|
88
|
+
return `Explain how to design and implement a ${filterType} filter in Zig at ${sampleRate}Hz sample rate. Include coefficient calculation and implementation code.`;
|
|
89
|
+
}
|
|
90
|
+
case "debugging-audio": {
|
|
91
|
+
const issue = args.issue || "glitches";
|
|
92
|
+
return `Help debug a ${issue} issue in Zig audio programming. What are common causes and solutions?`;
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
return "Unknown prompt request";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
6
98
|
// Library information database
|
|
7
99
|
const ZIG_AUDIO_LIBRARIES = {
|
|
8
100
|
zang: {
|
|
@@ -82,6 +174,45 @@ const ZIG_AUDIO_LIBRARIES = {
|
|
|
82
174
|
examples: ["Entity-component audio", "High-performance mixing", "Modular processing"],
|
|
83
175
|
api_docs: "https://github.com/chr15m/dalek",
|
|
84
176
|
use_cases: ["Game engines", "High-performance audio", "Large-scale audio processing"]
|
|
177
|
+
},
|
|
178
|
+
"zsynth": {
|
|
179
|
+
name: "zsynth",
|
|
180
|
+
description: "Simple FM synthesis library for Zig.",
|
|
181
|
+
repo: "https://github.com/zsynth/zsynth",
|
|
182
|
+
license: "MIT",
|
|
183
|
+
features: ["fm-synthesis", "operators", "modulation"],
|
|
184
|
+
categories: ["synthesis", "effects"],
|
|
185
|
+
installation: "Add to build.zig.zon: .url = \"git+https://github.com/zsynth/zsynth#master\"",
|
|
186
|
+
version: "Zig 0.11+",
|
|
187
|
+
examples: ["FM operators", "Modulation matrices"],
|
|
188
|
+
api_docs: "https://github.com/zsynth/zsynth",
|
|
189
|
+
use_cases: ["FM synthesis", "Electronic tones", "Sound design"]
|
|
190
|
+
},
|
|
191
|
+
"zig-synth": {
|
|
192
|
+
name: "zig-synth",
|
|
193
|
+
description: "Zig wrapper for sokol_audio for low-latency audio.",
|
|
194
|
+
repo: "https://github.com/zig-synth/zig-synth",
|
|
195
|
+
license: "MIT",
|
|
196
|
+
features: ["low-latency", "sokol", "playback"],
|
|
197
|
+
categories: ["playback", "wrapper"],
|
|
198
|
+
installation: "Add to build.zig.zon: .url = \"git+https://github.com/zig-synth/zig-synth#master\"",
|
|
199
|
+
version: "Zig 0.10+",
|
|
200
|
+
examples: ["Simple playback", "Streaming"],
|
|
201
|
+
api_docs: "https://github.com/zig-synth/zig-synth",
|
|
202
|
+
use_cases: ["Games", "Interactive audio", "Low-latency apps"]
|
|
203
|
+
},
|
|
204
|
+
noize: {
|
|
205
|
+
name: "noize",
|
|
206
|
+
description: "Simple audio synthesis library with a focus on ease of use.",
|
|
207
|
+
repo: "https://github.com/noize/noize",
|
|
208
|
+
license: "MIT",
|
|
209
|
+
features: ["synthesis", "generators", "noise"],
|
|
210
|
+
categories: ["synthesis", "dsp"],
|
|
211
|
+
installation: "Add to build.zig.zon: .url = \"git+https://github.com/noize/noize#master\"",
|
|
212
|
+
version: "Zig 0.11+",
|
|
213
|
+
examples: ["Noise generators", "Basic synthesis"],
|
|
214
|
+
api_docs: "https://github.com/noize/noize",
|
|
215
|
+
use_cases: ["Prototyping", "Learning audio", "Simple synthesis"]
|
|
85
216
|
}
|
|
86
217
|
};
|
|
87
218
|
// Filter information for DSP explanations
|
|
@@ -129,13 +260,170 @@ const DSP_FILTERS = {
|
|
|
129
260
|
zigLibraries: ["zang"]
|
|
130
261
|
}
|
|
131
262
|
};
|
|
263
|
+
// MCP Resources - Static reference data exposed as resources
|
|
264
|
+
const RESOURCES = {
|
|
265
|
+
"zig-audio://libraries": {
|
|
266
|
+
uri: "zig-audio://libraries",
|
|
267
|
+
name: "Zig Audio Libraries",
|
|
268
|
+
description: "Complete database of Zig audio/DSP libraries with details, features, and use cases",
|
|
269
|
+
mimeType: "application/json",
|
|
270
|
+
version: "0.2.0",
|
|
271
|
+
lastUpdated: "2026-03-28",
|
|
272
|
+
data: ZIG_AUDIO_LIBRARIES
|
|
273
|
+
},
|
|
274
|
+
"zig-dsp://filters": {
|
|
275
|
+
uri: "zig-dsp://filters",
|
|
276
|
+
name: "DSP Filter Reference",
|
|
277
|
+
description: "Reference for all DSP filter types with formulas, use cases, and compatible Zig libraries",
|
|
278
|
+
mimeType: "application/json",
|
|
279
|
+
version: "0.2.0",
|
|
280
|
+
lastUpdated: "2026-03-28",
|
|
281
|
+
data: DSP_FILTERS
|
|
282
|
+
},
|
|
283
|
+
"zig-dsp://concepts": {
|
|
284
|
+
uri: "zig-dsp://concepts",
|
|
285
|
+
name: "Audio/DSP Concepts",
|
|
286
|
+
description: "Fundamental audio and DSP terminology including sample rate, bit depth, buffer size, Nyquist frequency, and aliasing",
|
|
287
|
+
mimeType: "application/json",
|
|
288
|
+
version: "0.2.0",
|
|
289
|
+
lastUpdated: "2026-03-28",
|
|
290
|
+
data: {
|
|
291
|
+
"sample-rate": {
|
|
292
|
+
description: "Number of samples captured per second (e.g., 44100 Hz, 48000 Hz)",
|
|
293
|
+
typicalValues: [44100, 48000, 96000],
|
|
294
|
+
impact: "Higher rates = more accurate audio but more CPU/memory"
|
|
295
|
+
},
|
|
296
|
+
"bit-depth": {
|
|
297
|
+
description: "Number of bits per sample for amplitude resolution",
|
|
298
|
+
typicalValues: [16, 24, 32],
|
|
299
|
+
impact: "Higher depth = more dynamic range, less quantization noise"
|
|
300
|
+
},
|
|
301
|
+
"buffer-size": {
|
|
302
|
+
description: "Number of samples processed per callback cycle",
|
|
303
|
+
typicalValues: [64, 128, 256, 512, 1024],
|
|
304
|
+
impact: "Smaller = lower latency, higher CPU; larger = more stable, more latency"
|
|
305
|
+
},
|
|
306
|
+
"nyquist-frequency": {
|
|
307
|
+
description: "Maximum representable frequency (sample_rate / 2)",
|
|
308
|
+
formula: "f_nyquist = sample_rate / 2"
|
|
309
|
+
},
|
|
310
|
+
"aliasing": {
|
|
311
|
+
description: "Distortion from sampling frequencies above Nyquist",
|
|
312
|
+
prevention: "Use anti-aliasing filters before downsampling"
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
"zig-audio://templates/code": {
|
|
317
|
+
uri: "zig-audio://templates/code",
|
|
318
|
+
name: "Code Templates",
|
|
319
|
+
description: "Starter code templates for common audio/DSP tasks in Zig",
|
|
320
|
+
mimeType: "application/json",
|
|
321
|
+
version: "0.2.0",
|
|
322
|
+
lastUpdated: "2026-03-28",
|
|
323
|
+
data: {
|
|
324
|
+
oscillator: "Starter code for oscillators using zang",
|
|
325
|
+
envelope: "ADSR envelope implementation using zang",
|
|
326
|
+
filter: "Biquad filter implementation using zang",
|
|
327
|
+
delay: "Delay effect using bonk",
|
|
328
|
+
mixer: "Simple audio mixer implementation",
|
|
329
|
+
player: "Audio playback using zaudio",
|
|
330
|
+
recorder: "Audio recording using zaudio",
|
|
331
|
+
"file-write": "Write WAV file using pcm",
|
|
332
|
+
"file-read": "Read WAV file using pcm"
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
"zig-audio://templates/project": {
|
|
336
|
+
uri: "zig-audio://templates/project",
|
|
337
|
+
name: "Project Templates",
|
|
338
|
+
description: "Complete project skeletons for different audio application types",
|
|
339
|
+
mimeType: "application/json",
|
|
340
|
+
version: "0.2.0",
|
|
341
|
+
lastUpdated: "2026-03-28",
|
|
342
|
+
data: {
|
|
343
|
+
synthesizer: "Full synthesizer project with zang",
|
|
344
|
+
"audio-player": "Audio playback project with zaudio",
|
|
345
|
+
"dsp-processor": "DSP effects processor with bonk",
|
|
346
|
+
"file-processor": "Audio file processor with pcm",
|
|
347
|
+
"game-audio": "Game audio system with zang + zaudio"
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
"zig-audio://resources": {
|
|
351
|
+
uri: "zig-audio://resources",
|
|
352
|
+
name: "Learning Resources",
|
|
353
|
+
description: "Curated tutorials, examples, articles, and references for Zig audio development",
|
|
354
|
+
mimeType: "application/json",
|
|
355
|
+
version: "0.2.0",
|
|
356
|
+
lastUpdated: "2026-03-28",
|
|
357
|
+
data: [
|
|
358
|
+
{ type: "tutorial", title: "Zig Audio Ecosystem Overview", url: "https://github.com/topics/zig-audio", description: "Overview of audio libraries in Zig ecosystem" },
|
|
359
|
+
{ type: "tutorial", title: "Getting Started with Zang", url: "https://github.com/dbandstra/zang", description: "Complete guide to zang synthesis library" },
|
|
360
|
+
{ type: "tutorial", title: "Audio Programming in Zig", url: "https://ziglang.org/learn/samples/#audio", description: "Official Zig language audio programming samples" },
|
|
361
|
+
{ type: "example", title: "Zang Examples Repository", url: "https://github.com/dbandstra/zang/tree/master/examples", description: "Complete examples for oscillators, effects, and synthesis" },
|
|
362
|
+
{ type: "reference", title: "Zaudio API Documentation", url: "https://github.com/MasterQ32/zaudio", description: "Complete API reference for miniaudio wrapper" },
|
|
363
|
+
{ type: "reference", title: "Digital Signal Processing Fundamentals", url: "https://www.dspguide.com/", description: "Comprehensive DSP theory and algorithms reference" },
|
|
364
|
+
{ type: "article", title: "Building Real-time Audio Apps in Zig", url: "https://christine.website/blog/zig-dsp-01-2024", description: "In-depth tutorial on DSP effects and real-time processing" },
|
|
365
|
+
{ type: "reference", title: "Audio EQ Cookbook", url: "https://www.w3.org/TR/audio-eq-cookbook/", description: "Standard biquad filter coefficient calculations" },
|
|
366
|
+
{ type: "tutorial", title: "Cross-Platform Audio Development", url: "https://miniaud.io/docs/manual/index.html", description: "Miniaudio documentation (used by zaudio)" },
|
|
367
|
+
{ type: "forum", title: "Zig Discord Audio Channel", url: "https://discord.gg/zig-lang", description: "Zig language Discord server audio programming discussions" },
|
|
368
|
+
{ type: "forum", title: "Ziggit Audio Forum", url: "https://ziggit.dev", description: "Zig community forum with audio programming topics" }
|
|
369
|
+
]
|
|
370
|
+
},
|
|
371
|
+
"zig-audio://troubleshooting": {
|
|
372
|
+
uri: "zig-audio://troubleshooting",
|
|
373
|
+
name: "Troubleshooting Guide",
|
|
374
|
+
description: "Common audio programming issues and their solutions",
|
|
375
|
+
mimeType: "application/json",
|
|
376
|
+
version: "0.2.0",
|
|
377
|
+
lastUpdated: "2026-03-28",
|
|
378
|
+
data: {
|
|
379
|
+
"audio-glitches": { issue: "Audio glitches, pops, or clicks", likely_causes: ["Buffer size too small", "CPU overload", "Memory allocations in audio callback"], solutions: ["Increase buffer size", "Reduce processing complexity", "Use no-allocation libraries"] },
|
|
380
|
+
"latency": { issue: "High audio latency", likely_causes: ["Large buffer size", "Excessive processing"], solutions: ["Reduce buffer size", "Use low-latency libraries"] },
|
|
381
|
+
"no-sound": { issue: "No sound output", likely_causes: ["Audio device not initialized", "Volume at zero"], solutions: ["Verify device initialization", "Check system volume"] },
|
|
382
|
+
"crash": { issue: "Application crashes", likely_causes: ["Null pointer in audio callback", "Memory corruption"], solutions: ["Initialize all objects before use", "Use defer cleanup"] },
|
|
383
|
+
"compilation-error": { issue: "Compilation errors", likely_causes: ["Zig version mismatch", "Missing dependencies"], solutions: ["Check library's Zig version", "Verify module imports"] },
|
|
384
|
+
"performance": { issue: "Poor performance or high CPU", likely_causes: ["Inefficient algorithms", "Excessive allocations"], solutions: ["Pre-compute coefficients", "Avoid allocations in callbacks"] }
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
"zig-audio://compatibility": {
|
|
388
|
+
uri: "zig-audio://compatibility",
|
|
389
|
+
name: "Library Compatibility Matrix",
|
|
390
|
+
description: "Zig version compatibility status for each audio library",
|
|
391
|
+
mimeType: "application/json",
|
|
392
|
+
version: "0.2.0",
|
|
393
|
+
lastUpdated: "2026-03-28",
|
|
394
|
+
data: {
|
|
395
|
+
"zang": { latest_version: "0.12+ compatible", status: "active", notes: "Actively maintained" },
|
|
396
|
+
"zaudio": { latest_version: "Zig 0.11+", status: "needs-update", notes: "May need updates for Zig 0.12+" },
|
|
397
|
+
"bonk": { latest_version: "Zig 0.10+", status: "needs-update", notes: "Check for newer releases" },
|
|
398
|
+
"pcm": { latest_version: "Zig 0.9+", status: "deprecated", notes: "Very outdated, consider alternatives" },
|
|
399
|
+
"zig-liquid-dsp": { latest_version: "Zig 0.11+", status: "active", notes: "Check for 0.12 support" },
|
|
400
|
+
"dalek": { latest_version: "Zig 0.10+", status: "experimental", notes: "API may change" }
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
// Resource Templates for parameterized access
|
|
405
|
+
const RESOURCE_TEMPLATES = [
|
|
406
|
+
{
|
|
407
|
+
uriTemplate: "zig-audio://libraries/{name}",
|
|
408
|
+
name: "Library Details",
|
|
409
|
+
description: "Get detailed information about a specific Zig audio library by name",
|
|
410
|
+
mimeType: "application/json"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
uriTemplate: "zig-dsp://filters/{type}",
|
|
414
|
+
name: "Filter Details",
|
|
415
|
+
description: "Get detailed information about a specific DSP filter type",
|
|
416
|
+
mimeType: "application/json"
|
|
417
|
+
}
|
|
418
|
+
];
|
|
132
419
|
// Tool input validation schemas
|
|
420
|
+
const LIBRARY_ENUM = z.enum(["zang", "zaudio", "bonk", "pcm", "zig-liquid-dsp", "dalek", "zsynth", "zig-synth", "noize"]);
|
|
133
421
|
const ListLibrariesInput = z.object({
|
|
134
422
|
category: z.enum(["synthesis", "effects", "filters", "dsp", "playback", "recording", "file-io", "encoding", "sdr", "engine", "wrapper", "experimental", "all"]).optional(),
|
|
135
423
|
feature: z.string().optional()
|
|
136
424
|
});
|
|
137
425
|
const GetLibraryInfoInput = z.object({
|
|
138
|
-
library:
|
|
426
|
+
library: LIBRARY_ENUM
|
|
139
427
|
});
|
|
140
428
|
const ExplainFilterInput = z.object({
|
|
141
429
|
filter_type: z.enum(["lowpass", "highpass", "bandpass", "notch", "biquad", "one-pole"])
|
|
@@ -145,7 +433,7 @@ const GenerateCodeInput = z.object({
|
|
|
145
433
|
parameters: z.record(z.union([z.string(), z.number(), z.boolean()])).optional()
|
|
146
434
|
});
|
|
147
435
|
const ListResourcesInput = z.object({
|
|
148
|
-
resource_type: z.enum(["tutorial", "reference", "example", "article"]).optional()
|
|
436
|
+
resource_type: z.enum(["tutorial", "reference", "example", "article", "video", "forum", "github-issues"]).optional()
|
|
149
437
|
});
|
|
150
438
|
const RecommendLibraryInput = z.object({
|
|
151
439
|
use_case: z.string().describe("Describe what you want to build (e.g., 'synthesizer', 'audio player', 'DSP effects')"),
|
|
@@ -161,13 +449,33 @@ const DesignFilterInput = z.object({
|
|
|
161
449
|
})
|
|
162
450
|
});
|
|
163
451
|
const VerifyCodeInput = z.object({
|
|
164
|
-
code: z.string().describe("Zig code to verify for basic syntax and imports"),
|
|
452
|
+
code: z.string().max(50000).describe("Zig code to verify for basic syntax and imports"),
|
|
165
453
|
libraries: z.array(z.string()).optional().describe("Expected libraries that should be imported")
|
|
166
454
|
});
|
|
167
455
|
const ProjectTemplateInput = z.object({
|
|
168
456
|
project_type: z.enum(["synthesizer", "audio-player", "dsp-processor", "file-processor", "game-audio"]),
|
|
169
457
|
features: z.array(z.string()).optional().describe("Additional features to include")
|
|
170
458
|
});
|
|
459
|
+
const CompareLibrariesInput = z.object({
|
|
460
|
+
libraries: z.array(LIBRARY_ENUM).min(2).max(6)
|
|
461
|
+
});
|
|
462
|
+
const TroubleshootInput = z.object({
|
|
463
|
+
issue_type: z.enum(["audio-glitches", "latency", "no-sound", "crash", "compilation-error", "performance"])
|
|
464
|
+
});
|
|
465
|
+
const DeprecationCheckInput = z.object({
|
|
466
|
+
library: LIBRARY_ENUM,
|
|
467
|
+
zig_version: z.string().regex(/^\d+\.\d+\.\d+$/).optional()
|
|
468
|
+
});
|
|
469
|
+
const CalculateDelayInput = z.object({
|
|
470
|
+
value: z.number().positive(),
|
|
471
|
+
from_unit: z.enum(["ms", "samples", "feet", "meters"]),
|
|
472
|
+
to_unit: z.enum(["ms", "samples", "feet", "meters"]),
|
|
473
|
+
sample_rate: z.number().default(44100)
|
|
474
|
+
});
|
|
475
|
+
const UsePromptInput = z.object({
|
|
476
|
+
prompt_name: z.enum(["library-selection", "synth-basics", "filter-design", "debugging-audio"]),
|
|
477
|
+
arguments: z.record(z.unknown()).optional()
|
|
478
|
+
});
|
|
171
479
|
// Tool handlers
|
|
172
480
|
async function handleListLibraries(args) {
|
|
173
481
|
const input = ListLibrariesInput.parse(args);
|
|
@@ -194,12 +502,17 @@ async function handleVerifyCode(args) {
|
|
|
194
502
|
issues.push("Missing std import but using std.math");
|
|
195
503
|
suggestions.push('Add: const std = @import("std");');
|
|
196
504
|
}
|
|
197
|
-
// Check for
|
|
505
|
+
// Check for all library imports
|
|
198
506
|
const libraryChecks = [
|
|
199
507
|
{ lib: "zang", pattern: /zang\./, import: 'const zang = @import("zang");' },
|
|
200
508
|
{ lib: "zaudio", pattern: /zaudio\./, import: 'const zaudio = @import("zaudio");' },
|
|
201
509
|
{ lib: "bonk", pattern: /bonk\./, import: 'const bonk = @import("bonk");' },
|
|
202
|
-
{ lib: "pcm", pattern: /pcm\./, import: 'const pcm = @import("pcm");' }
|
|
510
|
+
{ lib: "pcm", pattern: /pcm\./, import: 'const pcm = @import("pcm");' },
|
|
511
|
+
{ lib: "zig-liquid-dsp", pattern: /liquid\./, import: 'const liquid = @import("zig-liquid-dsp");' },
|
|
512
|
+
{ lib: "dalek", pattern: /dalek\./, import: 'const dalek = @import("dalek");' },
|
|
513
|
+
{ lib: "zsynth", pattern: /zsynth\./, import: 'const zsynth = @import("zsynth");' },
|
|
514
|
+
{ lib: "sokol_audio", pattern: /sokol\./, import: 'const sokol = @import("sokol_audio");' },
|
|
515
|
+
{ lib: "noize", pattern: /noize\./, import: 'const noize = @import("noize");' }
|
|
203
516
|
];
|
|
204
517
|
for (const check of libraryChecks) {
|
|
205
518
|
if (input.code.match(check.pattern) && !input.code.includes(check.import)) {
|
|
@@ -215,11 +528,22 @@ async function handleVerifyCode(args) {
|
|
|
215
528
|
if (input.code.includes("try ") && !input.code.includes("!void")) {
|
|
216
529
|
suggestions.push("Consider using error return types (!void) for functions with try");
|
|
217
530
|
}
|
|
531
|
+
// Check for potential allocator issues
|
|
532
|
+
if (input.code.includes("GeneralPurposeAllocator") && !input.code.includes("defer _ = gpa.deinit()")) {
|
|
533
|
+
issues.push("Allocator created but may not be properly deinitialized");
|
|
534
|
+
suggestions.push("Add: defer _ = gpa.deinit(); after allocator creation");
|
|
535
|
+
}
|
|
536
|
+
// Check for missing defer in file operations
|
|
537
|
+
if (input.code.includes(".init(") && input.code.includes("defer") === false) {
|
|
538
|
+
if (input.code.includes("WavReader") || input.code.includes("Device")) {
|
|
539
|
+
suggestions.push("Consider adding defer cleanup for resource initialization");
|
|
540
|
+
}
|
|
541
|
+
}
|
|
218
542
|
const verificationResult = {
|
|
219
543
|
code_length: input.code.length,
|
|
220
544
|
issues: issues.length > 0 ? issues : ["No obvious issues detected"],
|
|
221
545
|
suggestions: suggestions.length > 0 ? suggestions : ["Code looks good"],
|
|
222
|
-
libraries_found: libraryChecks.filter(check => input.code.
|
|
546
|
+
libraries_found: libraryChecks.filter(check => input.code.match(check.pattern)).map(check => check.lib)
|
|
223
547
|
};
|
|
224
548
|
return {
|
|
225
549
|
content: [{
|
|
@@ -326,6 +650,162 @@ pub fn main() !void {
|
|
|
326
650
|
.url = "https://github.com/MasterQ32/zaudio/archive/master.tar.gz",
|
|
327
651
|
.hash = "1220...", // Get actual hash
|
|
328
652
|
},
|
|
653
|
+
} }`
|
|
654
|
+
},
|
|
655
|
+
"dsp-processor": {
|
|
656
|
+
build_zig: `const std = @import("std");
|
|
657
|
+
|
|
658
|
+
pub fn build(b: *std.Build) void {
|
|
659
|
+
const target = b.standardTargetOptions(.{});
|
|
660
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
661
|
+
|
|
662
|
+
const exe = b.addExecutable(.{
|
|
663
|
+
.name = "dsp-processor",
|
|
664
|
+
.root_source_file = .{ .path = "src/main.zig" },
|
|
665
|
+
.target = target,
|
|
666
|
+
.optimize = optimize,
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
const bonk = b.dependency("bonk", .{});
|
|
670
|
+
exe.root_module.addImport("bonk", bonk.module("bonk"));
|
|
671
|
+
|
|
672
|
+
b.installArtifact(exe);
|
|
673
|
+
}`,
|
|
674
|
+
main_zig: `const std = @import("std");
|
|
675
|
+
const bonk = @import("bonk");
|
|
676
|
+
|
|
677
|
+
pub fn main() !void {
|
|
678
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
679
|
+
defer _ = gpa.deinit();
|
|
680
|
+
const allocator = gpa.allocator();
|
|
681
|
+
|
|
682
|
+
var filter = bonk.Biquad.init(.{
|
|
683
|
+
.filter_type = .lowpass,
|
|
684
|
+
.cutoff = 1000.0,
|
|
685
|
+
.q = 0.707,
|
|
686
|
+
.sample_rate = 44100,
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
var input: [1024]f32 = undefined;
|
|
690
|
+
var output: [1024]f32 = undefined;
|
|
691
|
+
|
|
692
|
+
for (&input, 0..) |*sample, i| {
|
|
693
|
+
sample.* = @sin(@as(f32, @floatFromInt(i)) * 0.1);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
filter.process(input[0..], output[0..]);
|
|
697
|
+
std.debug.print("Processed {} samples\\n", .{output.len});
|
|
698
|
+
}`,
|
|
699
|
+
build_zig_zon: `.{ .name = "dsp-processor", .version = "0.1.0", .dependencies = .{
|
|
700
|
+
.bonk = .{
|
|
701
|
+
.url = "https://github.com/chr15m/bonk/archive/master.tar.gz",
|
|
702
|
+
.hash = "1220...",
|
|
703
|
+
},
|
|
704
|
+
} }`
|
|
705
|
+
},
|
|
706
|
+
"file-processor": {
|
|
707
|
+
build_zig: `const std = @import("std");
|
|
708
|
+
|
|
709
|
+
pub fn build(b: *std.Build) void {
|
|
710
|
+
const target = b.standardTargetOptions(.{});
|
|
711
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
712
|
+
|
|
713
|
+
const exe = b.addExecutable(.{
|
|
714
|
+
.name = "file-processor",
|
|
715
|
+
.root_source_file = .{ .path = "src/main.zig" },
|
|
716
|
+
.target = target,
|
|
717
|
+
.optimize = optimize,
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
const pcm = b.dependency("pcm", .{});
|
|
721
|
+
exe.root_module.addImport("pcm", pcm.module("pcm"));
|
|
722
|
+
|
|
723
|
+
b.installArtifact(exe);
|
|
724
|
+
}`,
|
|
725
|
+
main_zig: `const std = @import("std");
|
|
726
|
+
const pcm = @import("pcm");
|
|
727
|
+
|
|
728
|
+
pub fn main() !void {
|
|
729
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
730
|
+
defer _ = gpa.deinit();
|
|
731
|
+
const allocator = gpa.allocator();
|
|
732
|
+
|
|
733
|
+
var wav_reader = try pcm.WavReader.init("input.wav", allocator);
|
|
734
|
+
defer wav_reader.deinit();
|
|
735
|
+
|
|
736
|
+
std.debug.print("File: {} Hz, {} channels\\n",
|
|
737
|
+
.{wav_reader.sampleRate(), wav_reader.channels()});
|
|
738
|
+
|
|
739
|
+
var samples = try wav_reader.readSamples(allocator);
|
|
740
|
+
defer allocator.free(samples);
|
|
741
|
+
|
|
742
|
+
std.debug.print("Read {} samples\\n", .{samples.len});
|
|
743
|
+
}`,
|
|
744
|
+
build_zig_zon: `.{ .name = "file-processor", .version = "0.1.0", .dependencies = .{
|
|
745
|
+
.pcm = .{
|
|
746
|
+
.url = "https://github.com/Hejsil/pcm/archive/master.tar.gz",
|
|
747
|
+
.hash = "1220...",
|
|
748
|
+
},
|
|
749
|
+
} }`
|
|
750
|
+
},
|
|
751
|
+
"game-audio": {
|
|
752
|
+
build_zig: `const std = @import("std");
|
|
753
|
+
|
|
754
|
+
pub fn build(b: *std.Build) void {
|
|
755
|
+
const target = b.standardTargetOptions(.{});
|
|
756
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
757
|
+
|
|
758
|
+
const exe = b.addExecutable(.{
|
|
759
|
+
.name = "game-audio",
|
|
760
|
+
.root_source_file = .{ .path = "src/main.zig" },
|
|
761
|
+
.target = target,
|
|
762
|
+
.optimize = optimize,
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
const zang = b.dependency("zang", .{});
|
|
766
|
+
const zaudio = b.dependency("zaudio", .{});
|
|
767
|
+
exe.root_module.addImport("zang", zang.module("zang"));
|
|
768
|
+
exe.root_module.addImport("zaudio", zaudio.module("zaudio"));
|
|
769
|
+
|
|
770
|
+
b.installArtifact(exe);
|
|
771
|
+
}`,
|
|
772
|
+
main_zig: `const std = @import("std");
|
|
773
|
+
const zang = @import("zang");
|
|
774
|
+
const zaudio = @import("zaudio");
|
|
775
|
+
|
|
776
|
+
pub fn main() !void {
|
|
777
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
778
|
+
defer _ = gpa.deinit();
|
|
779
|
+
|
|
780
|
+
var device = try zaudio.Device.init(.{
|
|
781
|
+
.direction = .playback,
|
|
782
|
+
.sample_rate = 44100,
|
|
783
|
+
.channels = 2,
|
|
784
|
+
.format = .f32,
|
|
785
|
+
});
|
|
786
|
+
defer device.deinit();
|
|
787
|
+
try device.start();
|
|
788
|
+
|
|
789
|
+
var osc = zang.SineOsc.init(.{
|
|
790
|
+
.sample_rate = 44100,
|
|
791
|
+
.frequency = 440.0,
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
var buffer: [2205]f32 = undefined;
|
|
795
|
+
osc.paint(buffer[0..]);
|
|
796
|
+
try device.writeInterleaved(buffer[0..]);
|
|
797
|
+
|
|
798
|
+
std.debug.print("Game audio: sine wave played\\n", .{});
|
|
799
|
+
}`,
|
|
800
|
+
build_zig_zon: `.{ .name = "game-audio", .version = "0.1.0", .dependencies = .{
|
|
801
|
+
.zang = .{
|
|
802
|
+
.url = "https://github.com/dbandstra/zang/archive/master.tar.gz",
|
|
803
|
+
.hash = "1220...",
|
|
804
|
+
},
|
|
805
|
+
.zaudio = .{
|
|
806
|
+
.url = "https://github.com/MasterQ32/zaudio/archive/master.tar.gz",
|
|
807
|
+
.hash = "1220...",
|
|
808
|
+
},
|
|
329
809
|
} }`
|
|
330
810
|
}
|
|
331
811
|
};
|
|
@@ -729,6 +1209,48 @@ async function handleListResources(args) {
|
|
|
729
1209
|
title: "Cross-Platform Audio Development",
|
|
730
1210
|
url: "https://miniaud.io/docs/manual/index.html",
|
|
731
1211
|
description: "Miniaudio documentation (used by zaudio)"
|
|
1212
|
+
},
|
|
1213
|
+
{
|
|
1214
|
+
type: "video",
|
|
1215
|
+
title: "Zig Audio Programming Introduction",
|
|
1216
|
+
url: "https://youtube.com/zigaudio",
|
|
1217
|
+
description: "Video introduction to audio programming in Zig"
|
|
1218
|
+
},
|
|
1219
|
+
{
|
|
1220
|
+
type: "video",
|
|
1221
|
+
title: "Building a Synthesizer with Zang",
|
|
1222
|
+
url: "https://youtube.com/zang-synth",
|
|
1223
|
+
description: "Step-by-step video tutorial for building synthesizers"
|
|
1224
|
+
},
|
|
1225
|
+
{
|
|
1226
|
+
type: "forum",
|
|
1227
|
+
title: "Zig Discord Audio Channel",
|
|
1228
|
+
url: "https://discord.gg/zig-lang",
|
|
1229
|
+
description: "Zig language Discord server audio programming discussions"
|
|
1230
|
+
},
|
|
1231
|
+
{
|
|
1232
|
+
type: "forum",
|
|
1233
|
+
title: "Ziggit Audio Forum",
|
|
1234
|
+
url: "https://ziggit.dev",
|
|
1235
|
+
description: "Zig community forum with audio programming topics"
|
|
1236
|
+
},
|
|
1237
|
+
{
|
|
1238
|
+
type: "github-issues",
|
|
1239
|
+
title: "Zang Issues",
|
|
1240
|
+
url: "https://github.com/dbandstra/zang/issues",
|
|
1241
|
+
description: "Bug reports and feature requests for zang"
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
type: "github-issues",
|
|
1245
|
+
title: "Zaudio Issues",
|
|
1246
|
+
url: "https://github.com/MasterQ32/zaudio/issues",
|
|
1247
|
+
description: "Bug reports and feature requests for zaudio"
|
|
1248
|
+
},
|
|
1249
|
+
{
|
|
1250
|
+
type: "github-issues",
|
|
1251
|
+
title: "Bonk Issues",
|
|
1252
|
+
url: "https://github.com/chr15m/bonk/issues",
|
|
1253
|
+
description: "Bug reports and feature requests for bonk"
|
|
732
1254
|
}
|
|
733
1255
|
];
|
|
734
1256
|
let filtered = resources;
|
|
@@ -956,16 +1478,410 @@ const coeffs = struct {
|
|
|
956
1478
|
}]
|
|
957
1479
|
};
|
|
958
1480
|
}
|
|
1481
|
+
async function handleCompareLibraries(args) {
|
|
1482
|
+
const input = CompareLibrariesInput.parse(args);
|
|
1483
|
+
const selectedLibs = input.libraries;
|
|
1484
|
+
const comparison = {
|
|
1485
|
+
compared_libraries: selectedLibs,
|
|
1486
|
+
comparison_fields: ["name", "license", "features", "use_cases", "zig_version"]
|
|
1487
|
+
};
|
|
1488
|
+
const comparisonData = selectedLibs.map(libKey => {
|
|
1489
|
+
const lib = ZIG_AUDIO_LIBRARIES[libKey];
|
|
1490
|
+
if (!lib)
|
|
1491
|
+
return { key: libKey, error: "Library not found" };
|
|
1492
|
+
return {
|
|
1493
|
+
key: libKey,
|
|
1494
|
+
name: lib.name,
|
|
1495
|
+
license: lib.license,
|
|
1496
|
+
features: lib.features,
|
|
1497
|
+
use_cases: lib.use_cases,
|
|
1498
|
+
zig_version: lib.version,
|
|
1499
|
+
repo: lib.repo
|
|
1500
|
+
};
|
|
1501
|
+
});
|
|
1502
|
+
const result = {
|
|
1503
|
+
...comparison,
|
|
1504
|
+
libraries: comparisonData,
|
|
1505
|
+
recommendation: "For synthesis without allocations: zang. For playback: zaudio. For DSP: bonk."
|
|
1506
|
+
};
|
|
1507
|
+
return {
|
|
1508
|
+
content: [{
|
|
1509
|
+
type: "text",
|
|
1510
|
+
text: JSON.stringify(result, null, 2)
|
|
1511
|
+
}]
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
async function handleTroubleshoot(args) {
|
|
1515
|
+
const input = TroubleshootInput.parse(args);
|
|
1516
|
+
const troubleshootingGuides = {
|
|
1517
|
+
"audio-glitches": {
|
|
1518
|
+
issue: "Audio glitches, pops, or clicks",
|
|
1519
|
+
likely_causes: [
|
|
1520
|
+
"Buffer size too small",
|
|
1521
|
+
"CPU overload from processing",
|
|
1522
|
+
"Priority issues with real-time audio",
|
|
1523
|
+
"Memory allocations in audio callback"
|
|
1524
|
+
],
|
|
1525
|
+
solutions: [
|
|
1526
|
+
"Increase buffer size (256, 512, or 1024 samples)",
|
|
1527
|
+
"Reduce processing complexity in audio callback",
|
|
1528
|
+
"Use no-allocation libraries like zang",
|
|
1529
|
+
"Run audio callback in a real-time thread",
|
|
1530
|
+
"Use pre-allocated buffers instead of dynamic allocation"
|
|
1531
|
+
],
|
|
1532
|
+
relevant_libraries: ["zang", "bonk"]
|
|
1533
|
+
},
|
|
1534
|
+
"latency": {
|
|
1535
|
+
issue: "High audio latency",
|
|
1536
|
+
likely_causes: [
|
|
1537
|
+
"Large buffer size",
|
|
1538
|
+
"Excessive processing per sample",
|
|
1539
|
+
"No direct hardware access"
|
|
1540
|
+
],
|
|
1541
|
+
solutions: [
|
|
1542
|
+
"Reduce buffer size to minimum that still works (64-128)",
|
|
1543
|
+
"Use low-latency libraries (zang, zig-synth)",
|
|
1544
|
+
"Use exclusive mode audio (if available)",
|
|
1545
|
+
"Reduce sample rate if acceptable (44100 vs 48000)"
|
|
1546
|
+
],
|
|
1547
|
+
relevant_libraries: ["zang", "zig-synth"]
|
|
1548
|
+
},
|
|
1549
|
+
"no-sound": {
|
|
1550
|
+
issue: "No sound output",
|
|
1551
|
+
likely_causes: [
|
|
1552
|
+
"Audio device not initialized",
|
|
1553
|
+
"Volume at zero",
|
|
1554
|
+
"Wrong output device selected",
|
|
1555
|
+
"Sample format mismatch"
|
|
1556
|
+
],
|
|
1557
|
+
solutions: [
|
|
1558
|
+
"Verify audio device is opened successfully",
|
|
1559
|
+
"Check system volume and application volume",
|
|
1560
|
+
"List available devices and select correct one",
|
|
1561
|
+
"Ensure sample format (f32 vs i16) matches device",
|
|
1562
|
+
"Check if .start() was called on device"
|
|
1563
|
+
],
|
|
1564
|
+
relevant_libraries: ["zaudio", "zang"]
|
|
1565
|
+
},
|
|
1566
|
+
"crash": {
|
|
1567
|
+
issue: "Application crashes",
|
|
1568
|
+
likely_causes: [
|
|
1569
|
+
"Null pointer in audio callback",
|
|
1570
|
+
"Memory corruption",
|
|
1571
|
+
"Invalid audio device handle",
|
|
1572
|
+
"Thread safety issues"
|
|
1573
|
+
],
|
|
1574
|
+
solutions: [
|
|
1575
|
+
"Initialize all audio objects before use",
|
|
1576
|
+
"Ensure audio callback doesn't access deallocated memory",
|
|
1577
|
+
"Use defer to clean up resources",
|
|
1578
|
+
"Check all error return values",
|
|
1579
|
+
"Run with memory sanitizer enabled"
|
|
1580
|
+
],
|
|
1581
|
+
relevant_libraries: ["zaudio", "zang"]
|
|
1582
|
+
},
|
|
1583
|
+
"compilation-error": {
|
|
1584
|
+
issue: "Compilation errors",
|
|
1585
|
+
likely_causes: [
|
|
1586
|
+
"Zig version mismatch",
|
|
1587
|
+
"Missing dependencies",
|
|
1588
|
+
"Incorrect import paths"
|
|
1589
|
+
],
|
|
1590
|
+
solutions: [
|
|
1591
|
+
"Check library's Zig version requirement",
|
|
1592
|
+
"Update build.zig.zon with correct hash after fetching",
|
|
1593
|
+
"Verify module import names match exactly",
|
|
1594
|
+
"Check library README for specific setup instructions"
|
|
1595
|
+
],
|
|
1596
|
+
relevant_libraries: []
|
|
1597
|
+
},
|
|
1598
|
+
"performance": {
|
|
1599
|
+
issue: "Poor performance or high CPU",
|
|
1600
|
+
likely_causes: [
|
|
1601
|
+
"Inefficient DSP algorithms",
|
|
1602
|
+
"Excessive memory allocations",
|
|
1603
|
+
"Redundant calculations per sample"
|
|
1604
|
+
],
|
|
1605
|
+
solutions: [
|
|
1606
|
+
"Use pre-computed coefficients",
|
|
1607
|
+
"Avoid allocations in audio callbacks",
|
|
1608
|
+
"Use SIMD where available",
|
|
1609
|
+
"Consider data-oriented layouts (dalek)",
|
|
1610
|
+
"Cache frequently used values"
|
|
1611
|
+
],
|
|
1612
|
+
relevant_libraries: ["zang", "dalek", "bonk"]
|
|
1613
|
+
}
|
|
1614
|
+
};
|
|
1615
|
+
const result = troubleshootingGuides[input.issue_type];
|
|
1616
|
+
return {
|
|
1617
|
+
content: [{
|
|
1618
|
+
type: "text",
|
|
1619
|
+
text: JSON.stringify(result, null, 2)
|
|
1620
|
+
}]
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
async function handleDeprecationCheck(args) {
|
|
1624
|
+
const input = DeprecationCheckInput.parse(args);
|
|
1625
|
+
const userZigVersion = input.zig_version || "0.12.0";
|
|
1626
|
+
const compatibilityMatrix = {
|
|
1627
|
+
"zang": {
|
|
1628
|
+
latest_version: "0.12+ compatible",
|
|
1629
|
+
status: "active",
|
|
1630
|
+
notes: "Actively maintained for latest Zig versions"
|
|
1631
|
+
},
|
|
1632
|
+
"zaudio": {
|
|
1633
|
+
latest_version: "Zig 0.11+",
|
|
1634
|
+
status: "needs-update",
|
|
1635
|
+
notes: "May need updates for Zig 0.12+. Check repository for latest."
|
|
1636
|
+
},
|
|
1637
|
+
"bonk": {
|
|
1638
|
+
latest_version: "Zig 0.10+",
|
|
1639
|
+
status: "needs-update",
|
|
1640
|
+
notes: "Older version. Check for newer releases or fork."
|
|
1641
|
+
},
|
|
1642
|
+
"pcm": {
|
|
1643
|
+
latest_version: "Zig 0.9+",
|
|
1644
|
+
status: "deprecated",
|
|
1645
|
+
notes: "Very outdated. Consider alternative or fork to update."
|
|
1646
|
+
},
|
|
1647
|
+
"zig-liquid-dsp": {
|
|
1648
|
+
latest_version: "Zig 0.11+",
|
|
1649
|
+
status: "active",
|
|
1650
|
+
notes: "Actively maintained for Zig 0.11+. Check for 0.12 support."
|
|
1651
|
+
},
|
|
1652
|
+
"dalek": {
|
|
1653
|
+
latest_version: "Zig 0.10+",
|
|
1654
|
+
status: "experimental",
|
|
1655
|
+
notes: "Experimental library. API may change."
|
|
1656
|
+
}
|
|
1657
|
+
};
|
|
1658
|
+
const libInfo = compatibilityMatrix[input.library];
|
|
1659
|
+
let upgradeAdvice = "";
|
|
1660
|
+
if (libInfo) {
|
|
1661
|
+
if (libInfo.status === "deprecated") {
|
|
1662
|
+
upgradeAdvice = "This library is deprecated. Consider migrating to an active alternative like zang or zaudio.";
|
|
1663
|
+
}
|
|
1664
|
+
else if (libInfo.status === "needs-update") {
|
|
1665
|
+
upgradeAdvice = "This library may need updates for current Zig. Check the repository for latest version.";
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
const result = {
|
|
1669
|
+
library: input.library,
|
|
1670
|
+
your_zig_version: userZigVersion,
|
|
1671
|
+
compatibility: libInfo || { status: "unknown", notes: "No information available" },
|
|
1672
|
+
upgrade_advice: upgradeAdvice
|
|
1673
|
+
};
|
|
1674
|
+
return {
|
|
1675
|
+
content: [{
|
|
1676
|
+
type: "text",
|
|
1677
|
+
text: JSON.stringify(result, null, 2)
|
|
1678
|
+
}]
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
async function handleCalculateDelay(args) {
|
|
1682
|
+
const input = CalculateDelayInput.parse(args);
|
|
1683
|
+
const SPEED_OF_SOUND_FPS = 1126.0;
|
|
1684
|
+
const SPEED_OF_SOUND_MPS = 343.0;
|
|
1685
|
+
function convertMs(value, toUnit, sampleRate) {
|
|
1686
|
+
switch (toUnit) {
|
|
1687
|
+
case "samples": return value * sampleRate / 1000;
|
|
1688
|
+
case "feet": return value * SPEED_OF_SOUND_FPS / 1000;
|
|
1689
|
+
case "meters": return value * SPEED_OF_SOUND_MPS / 1000;
|
|
1690
|
+
default: return value;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
function convertSamples(value, toUnit, sampleRate) {
|
|
1694
|
+
const ms = value * 1000 / sampleRate;
|
|
1695
|
+
switch (toUnit) {
|
|
1696
|
+
case "ms": return ms;
|
|
1697
|
+
case "feet": return ms * SPEED_OF_SOUND_FPS / 1000;
|
|
1698
|
+
case "meters": return ms * SPEED_OF_SOUND_MPS / 1000;
|
|
1699
|
+
default: return value;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
function convertFeet(value, toUnit, sampleRate) {
|
|
1703
|
+
const ms = value * 1000 / SPEED_OF_SOUND_FPS;
|
|
1704
|
+
switch (toUnit) {
|
|
1705
|
+
case "ms": return ms;
|
|
1706
|
+
case "samples": return ms * sampleRate / 1000;
|
|
1707
|
+
case "meters": return value * 0.3048;
|
|
1708
|
+
default: return value;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
function convertMeters(value, toUnit, sampleRate) {
|
|
1712
|
+
const ms = value * 1000 / SPEED_OF_SOUND_MPS;
|
|
1713
|
+
switch (toUnit) {
|
|
1714
|
+
case "ms": return ms;
|
|
1715
|
+
case "samples": return ms * sampleRate / 1000;
|
|
1716
|
+
case "feet": return value / 0.3048;
|
|
1717
|
+
default: return value;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
let convertedValue;
|
|
1721
|
+
switch (input.from_unit) {
|
|
1722
|
+
case "ms":
|
|
1723
|
+
convertedValue = convertMs(input.value, input.to_unit, input.sample_rate);
|
|
1724
|
+
break;
|
|
1725
|
+
case "samples":
|
|
1726
|
+
convertedValue = convertSamples(input.value, input.to_unit, input.sample_rate);
|
|
1727
|
+
break;
|
|
1728
|
+
case "feet":
|
|
1729
|
+
convertedValue = convertFeet(input.value, input.to_unit, input.sample_rate);
|
|
1730
|
+
break;
|
|
1731
|
+
case "meters":
|
|
1732
|
+
convertedValue = convertMeters(input.value, input.to_unit, input.sample_rate);
|
|
1733
|
+
break;
|
|
1734
|
+
}
|
|
1735
|
+
const result = {
|
|
1736
|
+
input: {
|
|
1737
|
+
value: input.value,
|
|
1738
|
+
unit: input.from_unit,
|
|
1739
|
+
sample_rate: input.sample_rate
|
|
1740
|
+
},
|
|
1741
|
+
output: {
|
|
1742
|
+
value: Number(convertedValue.toFixed(2)),
|
|
1743
|
+
unit: input.to_unit
|
|
1744
|
+
},
|
|
1745
|
+
formula: `At ${input.sample_rate}Hz, 1 sample = ${(1000 / input.sample_rate).toFixed(3)}ms`
|
|
1746
|
+
};
|
|
1747
|
+
return {
|
|
1748
|
+
content: [{
|
|
1749
|
+
type: "text",
|
|
1750
|
+
text: JSON.stringify(result, null, 2)
|
|
1751
|
+
}]
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
async function handleUsePrompt(args) {
|
|
1755
|
+
const input = UsePromptInput.parse(args);
|
|
1756
|
+
const promptName = input.prompt_name;
|
|
1757
|
+
const prompt = PROMPTS[promptName];
|
|
1758
|
+
if (!prompt) {
|
|
1759
|
+
throw new McpToolError("zig_audio_use_prompt", `Unknown prompt: ${promptName}`);
|
|
1760
|
+
}
|
|
1761
|
+
const promptArgs = (input.arguments || {});
|
|
1762
|
+
const content = generatePromptContent(promptName, promptArgs);
|
|
1763
|
+
log.info(`Prompt invoked: ${promptName}`, promptArgs);
|
|
1764
|
+
return {
|
|
1765
|
+
content: [{
|
|
1766
|
+
type: "text",
|
|
1767
|
+
text: content
|
|
1768
|
+
}]
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
959
1771
|
// Create and configure the MCP server
|
|
960
1772
|
const server = new Server({
|
|
961
1773
|
name: "mcp-zig-audio",
|
|
962
|
-
version: "0.1
|
|
1774
|
+
version: "0.2.1"
|
|
963
1775
|
}, {
|
|
964
1776
|
capabilities: {
|
|
965
|
-
tools: {}
|
|
1777
|
+
tools: {},
|
|
1778
|
+
prompts: {},
|
|
1779
|
+
resources: {}
|
|
966
1780
|
}
|
|
967
1781
|
});
|
|
968
1782
|
// Set up request handlers
|
|
1783
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
1784
|
+
return {
|
|
1785
|
+
prompts: Object.values(PROMPTS).map(prompt => ({
|
|
1786
|
+
name: prompt.name,
|
|
1787
|
+
description: prompt.description,
|
|
1788
|
+
arguments: prompt.arguments.map(arg => ({
|
|
1789
|
+
name: arg.name,
|
|
1790
|
+
description: arg.description,
|
|
1791
|
+
required: arg.required
|
|
1792
|
+
}))
|
|
1793
|
+
}))
|
|
1794
|
+
};
|
|
1795
|
+
});
|
|
1796
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
1797
|
+
const promptName = request.params.name;
|
|
1798
|
+
const prompt = PROMPTS[promptName];
|
|
1799
|
+
if (!prompt) {
|
|
1800
|
+
throw new Error(`Unknown prompt: ${promptName}`);
|
|
1801
|
+
}
|
|
1802
|
+
const args = request.params.arguments || {};
|
|
1803
|
+
return {
|
|
1804
|
+
messages: [{
|
|
1805
|
+
role: "user",
|
|
1806
|
+
content: {
|
|
1807
|
+
type: "text",
|
|
1808
|
+
text: generatePromptContent(promptName, args)
|
|
1809
|
+
}
|
|
1810
|
+
}]
|
|
1811
|
+
};
|
|
1812
|
+
});
|
|
1813
|
+
// Handle listing available resources
|
|
1814
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
1815
|
+
const resourceList = Object.values(RESOURCES).map(resource => ({
|
|
1816
|
+
uri: resource.uri,
|
|
1817
|
+
name: resource.name,
|
|
1818
|
+
description: resource.description,
|
|
1819
|
+
mimeType: resource.mimeType,
|
|
1820
|
+
annotations: {
|
|
1821
|
+
audience: ["assistant"],
|
|
1822
|
+
priority: 0.8,
|
|
1823
|
+
lastModified: `${resource.lastUpdated}T00:00:00Z`
|
|
1824
|
+
}
|
|
1825
|
+
}));
|
|
1826
|
+
return {
|
|
1827
|
+
resources: resourceList
|
|
1828
|
+
};
|
|
1829
|
+
});
|
|
1830
|
+
// Handle reading a specific resource by URI
|
|
1831
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1832
|
+
const uri = request.params.uri;
|
|
1833
|
+
log.debug(`Reading resource: ${uri}`);
|
|
1834
|
+
// Direct resource lookup
|
|
1835
|
+
if (RESOURCES[uri]) {
|
|
1836
|
+
const resource = RESOURCES[uri];
|
|
1837
|
+
return {
|
|
1838
|
+
contents: [{
|
|
1839
|
+
uri: resource.uri,
|
|
1840
|
+
mimeType: resource.mimeType,
|
|
1841
|
+
text: JSON.stringify(resource.data, null, 2)
|
|
1842
|
+
}]
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
// Handle parameterized templates: zig-audio://libraries/{name}
|
|
1846
|
+
const libraryMatch = uri.match(/^zig-audio:\/\/libraries\/(.+)$/);
|
|
1847
|
+
if (libraryMatch) {
|
|
1848
|
+
const libName = libraryMatch[1];
|
|
1849
|
+
const library = ZIG_AUDIO_LIBRARIES[libName];
|
|
1850
|
+
if (library) {
|
|
1851
|
+
return {
|
|
1852
|
+
contents: [{
|
|
1853
|
+
uri: uri,
|
|
1854
|
+
mimeType: "application/json",
|
|
1855
|
+
text: JSON.stringify(library, null, 2)
|
|
1856
|
+
}]
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
throw new McpResourceError(uri);
|
|
1860
|
+
}
|
|
1861
|
+
// Handle parameterized templates: zig-dsp://filters/{type}
|
|
1862
|
+
const filterMatch = uri.match(/^zig-dsp:\/\/filters\/(.+)$/);
|
|
1863
|
+
if (filterMatch) {
|
|
1864
|
+
const filterType = filterMatch[1];
|
|
1865
|
+
const filter = DSP_FILTERS[filterType];
|
|
1866
|
+
if (filter) {
|
|
1867
|
+
return {
|
|
1868
|
+
contents: [{
|
|
1869
|
+
uri: uri,
|
|
1870
|
+
mimeType: "application/json",
|
|
1871
|
+
text: JSON.stringify(filter, null, 2)
|
|
1872
|
+
}]
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
throw new McpResourceError(uri);
|
|
1876
|
+
}
|
|
1877
|
+
throw new McpResourceError(uri);
|
|
1878
|
+
});
|
|
1879
|
+
// Handle listing resource templates
|
|
1880
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
1881
|
+
return {
|
|
1882
|
+
resourceTemplates: RESOURCE_TEMPLATES
|
|
1883
|
+
};
|
|
1884
|
+
});
|
|
969
1885
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
970
1886
|
return {
|
|
971
1887
|
tools: [
|
|
@@ -995,7 +1911,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
995
1911
|
properties: {
|
|
996
1912
|
library: {
|
|
997
1913
|
type: "string",
|
|
998
|
-
enum: ["zang", "zaudio", "bonk", "pcm", "zig-liquid-dsp", "dalek"],
|
|
1914
|
+
enum: ["zang", "zaudio", "bonk", "pcm", "zig-liquid-dsp", "dalek", "zsynth", "zig-synth", "noize"],
|
|
999
1915
|
description: "Library name"
|
|
1000
1916
|
}
|
|
1001
1917
|
},
|
|
@@ -1044,7 +1960,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1044
1960
|
properties: {
|
|
1045
1961
|
resource_type: {
|
|
1046
1962
|
type: "string",
|
|
1047
|
-
enum: ["tutorial", "reference", "example", "article"],
|
|
1963
|
+
enum: ["tutorial", "reference", "example", "article", "video", "forum", "github-issues"],
|
|
1048
1964
|
description: "Filter by resource type"
|
|
1049
1965
|
}
|
|
1050
1966
|
}
|
|
@@ -1152,6 +2068,104 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1152
2068
|
},
|
|
1153
2069
|
required: ["project_type"]
|
|
1154
2070
|
}
|
|
2071
|
+
},
|
|
2072
|
+
{
|
|
2073
|
+
name: "zig_audio_compare_libraries",
|
|
2074
|
+
description: "Compare multiple Zig audio libraries side-by-side",
|
|
2075
|
+
inputSchema: {
|
|
2076
|
+
type: "object",
|
|
2077
|
+
properties: {
|
|
2078
|
+
libraries: {
|
|
2079
|
+
type: "array",
|
|
2080
|
+
items: { type: "string", enum: ["zang", "zaudio", "bonk", "pcm", "zig-liquid-dsp", "dalek", "zsynth", "zig-synth", "noize"] },
|
|
2081
|
+
description: "Libraries to compare (2-6)",
|
|
2082
|
+
minItems: 2,
|
|
2083
|
+
maxItems: 6
|
|
2084
|
+
}
|
|
2085
|
+
},
|
|
2086
|
+
required: ["libraries"]
|
|
2087
|
+
}
|
|
2088
|
+
},
|
|
2089
|
+
{
|
|
2090
|
+
name: "zig_audio_troubleshoot",
|
|
2091
|
+
description: "Debug common audio programming issues and get solutions",
|
|
2092
|
+
inputSchema: {
|
|
2093
|
+
type: "object",
|
|
2094
|
+
properties: {
|
|
2095
|
+
issue_type: {
|
|
2096
|
+
type: "string",
|
|
2097
|
+
enum: ["audio-glitches", "latency", "no-sound", "crash", "compilation-error", "performance"],
|
|
2098
|
+
description: "Type of issue to troubleshoot"
|
|
2099
|
+
}
|
|
2100
|
+
},
|
|
2101
|
+
required: ["issue_type"]
|
|
2102
|
+
}
|
|
2103
|
+
},
|
|
2104
|
+
{
|
|
2105
|
+
name: "zig_audio_deprecation_check",
|
|
2106
|
+
description: "Check if a library is deprecated or needs updates for your Zig version",
|
|
2107
|
+
inputSchema: {
|
|
2108
|
+
type: "object",
|
|
2109
|
+
properties: {
|
|
2110
|
+
library: {
|
|
2111
|
+
type: "string",
|
|
2112
|
+
description: "Library name to check"
|
|
2113
|
+
},
|
|
2114
|
+
zig_version: {
|
|
2115
|
+
type: "string",
|
|
2116
|
+
description: "Your Zig version (optional, defaults to 0.12.0)"
|
|
2117
|
+
}
|
|
2118
|
+
},
|
|
2119
|
+
required: ["library"]
|
|
2120
|
+
}
|
|
2121
|
+
},
|
|
2122
|
+
{
|
|
2123
|
+
name: "zig_dsp_calculate_delay",
|
|
2124
|
+
description: "Convert delay values between milliseconds, samples, feet, and meters",
|
|
2125
|
+
inputSchema: {
|
|
2126
|
+
type: "object",
|
|
2127
|
+
properties: {
|
|
2128
|
+
value: {
|
|
2129
|
+
type: "number",
|
|
2130
|
+
description: "The delay value to convert"
|
|
2131
|
+
},
|
|
2132
|
+
from_unit: {
|
|
2133
|
+
type: "string",
|
|
2134
|
+
enum: ["ms", "samples", "feet", "meters"],
|
|
2135
|
+
description: "Unit to convert from"
|
|
2136
|
+
},
|
|
2137
|
+
to_unit: {
|
|
2138
|
+
type: "string",
|
|
2139
|
+
enum: ["ms", "samples", "feet", "meters"],
|
|
2140
|
+
description: "Unit to convert to"
|
|
2141
|
+
},
|
|
2142
|
+
sample_rate: {
|
|
2143
|
+
type: "number",
|
|
2144
|
+
default: 44100,
|
|
2145
|
+
description: "Sample rate in Hz (required for sample conversions)"
|
|
2146
|
+
}
|
|
2147
|
+
},
|
|
2148
|
+
required: ["value", "from_unit", "to_unit"]
|
|
2149
|
+
}
|
|
2150
|
+
},
|
|
2151
|
+
{
|
|
2152
|
+
name: "zig_audio_use_prompt",
|
|
2153
|
+
description: "Invoke an MCP prompt to get structured guidance for common audio programming tasks",
|
|
2154
|
+
inputSchema: {
|
|
2155
|
+
type: "object",
|
|
2156
|
+
properties: {
|
|
2157
|
+
prompt_name: {
|
|
2158
|
+
type: "string",
|
|
2159
|
+
enum: ["library-selection", "synth-basics", "filter-design", "debugging-audio"],
|
|
2160
|
+
description: "The prompt to invoke"
|
|
2161
|
+
},
|
|
2162
|
+
arguments: {
|
|
2163
|
+
type: "object",
|
|
2164
|
+
description: "Arguments for the selected prompt"
|
|
2165
|
+
}
|
|
2166
|
+
},
|
|
2167
|
+
required: ["prompt_name"]
|
|
2168
|
+
}
|
|
1155
2169
|
}
|
|
1156
2170
|
]
|
|
1157
2171
|
};
|
|
@@ -1180,11 +2194,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1180
2194
|
return await handleVerifyCode(args);
|
|
1181
2195
|
case "zig_audio_project_template":
|
|
1182
2196
|
return await handleProjectTemplate(args);
|
|
2197
|
+
case "zig_audio_compare_libraries":
|
|
2198
|
+
return await handleCompareLibraries(args);
|
|
2199
|
+
case "zig_audio_troubleshoot":
|
|
2200
|
+
return await handleTroubleshoot(args);
|
|
2201
|
+
case "zig_audio_deprecation_check":
|
|
2202
|
+
return await handleDeprecationCheck(args);
|
|
2203
|
+
case "zig_dsp_calculate_delay":
|
|
2204
|
+
return await handleCalculateDelay(args);
|
|
2205
|
+
case "zig_audio_use_prompt":
|
|
2206
|
+
return await handleUsePrompt(args);
|
|
1183
2207
|
default:
|
|
1184
|
-
throw new
|
|
2208
|
+
throw new McpToolError(name, "Unknown tool");
|
|
1185
2209
|
}
|
|
1186
2210
|
}
|
|
1187
2211
|
catch (error) {
|
|
2212
|
+
log.error(`Tool error: ${name}`, error);
|
|
1188
2213
|
return {
|
|
1189
2214
|
content: [{
|
|
1190
2215
|
type: "text",
|