mcp-message-test 1.0.0

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.
@@ -0,0 +1,84 @@
1
+ import { z } from 'zod/v4';
2
+ import { annotationsSchema, structuredSchema } from '../schemas.js';
3
+ import { randInt, randPick, randSentence, randParagraph, buildAnnotations } from '../utils/index.js';
4
+ export function registerGenText(server) {
5
+ server.registerTool('gen_text', {
6
+ title: 'Generate Text',
7
+ description: '动态生成随机文本内容',
8
+ inputSchema: z.object({
9
+ format: z.enum(['plain', 'markdown', 'code', 'json']).default('plain').describe('文本格式'),
10
+ length: z.number().min(1).max(100000).default(200).describe('大致字符长度'),
11
+ count: z.number().min(1).max(100).default(1).describe('返回的 content item 数量'),
12
+ annotations: annotationsSchema,
13
+ structured: structuredSchema,
14
+ }),
15
+ }, async ({ format, length, count, annotations, structured }) => {
16
+ const generateText = (targetLen) => {
17
+ switch (format) {
18
+ case 'plain': {
19
+ let text = '';
20
+ while (text.length < targetLen)
21
+ text += randParagraph() + '\n\n';
22
+ return text.slice(0, targetLen);
23
+ }
24
+ case 'markdown': {
25
+ const sections = ['# ' + randSentence(), '', '## Overview', '', randParagraph(3), '',
26
+ '- ' + randSentence(), '- ' + randSentence(), '- ' + randSentence(), '',
27
+ '## Details', '', randParagraph(4), '',
28
+ '```typescript', `const value = ${randInt(1, 1000)};`, 'console.log(value);', '```', '',
29
+ '> ' + randSentence(), '', '| Column A | Column B |', '|----------|----------|',
30
+ `| ${randInt(1, 99)} | ${randSentence().slice(0, 20)} |`,
31
+ `| ${randInt(1, 99)} | ${randSentence().slice(0, 20)} |`];
32
+ let text = sections.join('\n');
33
+ while (text.length < targetLen)
34
+ text += '\n\n' + randParagraph();
35
+ return text.slice(0, targetLen);
36
+ }
37
+ case 'code': {
38
+ const langs = ['typescript', 'python', 'go', 'rust'];
39
+ const lang = randPick(langs);
40
+ const snippets = {
41
+ typescript: () => `interface ${randPick(['User', 'Config', 'State', 'Event'])} {\n id: string;\n name: string;\n value: ${randInt(1, 999)};\n active: boolean;\n}\n\nfunction process(input: string): number {\n const result = input.length * ${randInt(2, 10)};\n console.log(\`Processing: \${result}\`);\n return result;\n}`,
42
+ python: () => `class ${randPick(['DataProcessor', 'EventHandler', 'ConfigManager', 'StateManager'])}:\n def __init__(self, value: int = ${randInt(1, 100)}):\n self.value = value\n self._cache: dict = {}\n\n def process(self, data: list) -> dict:\n result = {k: v * self.value for k, v in enumerate(data)}\n return result`,
43
+ go: () => `package main\n\nimport "fmt"\n\ntype ${randPick(['Server', 'Client', 'Handler', 'Worker'])} struct {\n\tID int\n\tName string\n}\n\nfunc (s *Server) Run() error {\n\tfmt.Printf("Running %s (id=%d)\\n", s.Name, s.ID)\n\treturn nil\n}`,
44
+ rust: () => `use std::collections::HashMap;\n\nstruct ${randPick(['Cache', 'Registry', 'Pool', 'Queue'])}<T> {\n data: HashMap<String, T>,\n capacity: usize,\n}\n\nimpl<T> Cache<T> {\n fn new(cap: usize) -> Self {\n Self { data: HashMap::new(), capacity: cap }\n }\n}`,
45
+ };
46
+ let text = snippets[lang]();
47
+ while (text.length < targetLen)
48
+ text += '\n\n' + snippets[lang]();
49
+ return text.slice(0, targetLen);
50
+ }
51
+ case 'json': {
52
+ const obj = {
53
+ id: crypto.randomUUID(),
54
+ timestamp: new Date().toISOString(),
55
+ type: randPick(['event', 'message', 'notification', 'request']),
56
+ payload: {
57
+ values: Array.from({ length: randInt(3, 8) }, () => randInt(1, 1000)),
58
+ message: randSentence(),
59
+ metadata: { version: `${randInt(1, 5)}.${randInt(0, 9)}.${randInt(0, 20)}`, source: randPick(['api', 'webhook', 'cron', 'manual']) },
60
+ },
61
+ };
62
+ let text = JSON.stringify(obj, null, 2);
63
+ while (text.length < targetLen) {
64
+ obj.payload.values.push(randInt(1, 9999));
65
+ obj.payload.extra = randParagraph(2);
66
+ text = JSON.stringify(obj, null, 2);
67
+ }
68
+ return text.slice(0, targetLen);
69
+ }
70
+ }
71
+ };
72
+ const ann = buildAnnotations(annotations);
73
+ const content = Array.from({ length: count }, () => ({
74
+ type: 'text',
75
+ text: generateText(length),
76
+ ...(ann ? { annotations: ann } : {}),
77
+ }));
78
+ const result = { content };
79
+ if (structured) {
80
+ result.structuredContent = { format, length, count, generatedAt: new Date().toISOString() };
81
+ }
82
+ return result;
83
+ });
84
+ }
@@ -0,0 +1,5 @@
1
+ /** 构建 annotations 对象 */
2
+ export declare function buildAnnotations(input?: {
3
+ audience?: ('user' | 'assistant')[];
4
+ priority?: number;
5
+ }): Record<string, any> | undefined;
@@ -0,0 +1,11 @@
1
+ /** 构建 annotations 对象 */
2
+ export function buildAnnotations(input) {
3
+ if (!input)
4
+ return undefined;
5
+ const result = {};
6
+ if (input.audience)
7
+ result.audience = input.audience;
8
+ if (input.priority !== undefined)
9
+ result.priority = input.priority;
10
+ return Object.keys(result).length > 0 ? result : undefined;
11
+ }
@@ -0,0 +1,5 @@
1
+ export declare const assetsDir: string;
2
+ /** 读取 asset 文件并返回 base64 */
3
+ export declare function loadAssetBase64(filename: string): string;
4
+ /** 读取 asset 文件并返回 utf-8 文本 */
5
+ export declare function loadAssetText(filename: string): string;
@@ -0,0 +1,13 @@
1
+ import { readFileSync } from 'fs';
2
+ import { resolve, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ export const assetsDir = resolve(__dirname, '..', '..', 'assets');
6
+ /** 读取 asset 文件并返回 base64 */
7
+ export function loadAssetBase64(filename) {
8
+ return readFileSync(resolve(assetsDir, filename)).toString('base64');
9
+ }
10
+ /** 读取 asset 文件并返回 utf-8 文本 */
11
+ export function loadAssetText(filename) {
12
+ return readFileSync(resolve(assetsDir, filename), 'utf-8');
13
+ }
@@ -0,0 +1,4 @@
1
+ /** 生成正弦波 WAV Buffer */
2
+ export declare function generateWav(durationMs: number, frequency?: number, sampleRate?: number): Buffer;
3
+ /** 生成随机旋律的 WAV Buffer(带和弦伴奏) */
4
+ export declare function generateMelodyWav(durationMs: number, sampleRate?: number): Buffer;
@@ -0,0 +1,439 @@
1
+ import { randInt, randPick } from './random.js';
2
+ /** 生成正弦波 WAV Buffer */
3
+ export function generateWav(durationMs, frequency = 440, sampleRate = 44100) {
4
+ const numSamples = Math.floor(sampleRate * durationMs / 1000);
5
+ const dataSize = numSamples * 2; // 16-bit mono
6
+ const buf = Buffer.alloc(44 + dataSize);
7
+ // RIFF header
8
+ buf.write('RIFF', 0);
9
+ buf.writeUInt32LE(36 + dataSize, 4);
10
+ buf.write('WAVE', 8);
11
+ // fmt chunk
12
+ buf.write('fmt ', 12);
13
+ buf.writeUInt32LE(16, 16); // chunk size
14
+ buf.writeUInt16LE(1, 20); // PCM
15
+ buf.writeUInt16LE(1, 22); // mono
16
+ buf.writeUInt32LE(sampleRate, 24);
17
+ buf.writeUInt32LE(sampleRate * 2, 28); // byte rate
18
+ buf.writeUInt16LE(2, 32); // block align
19
+ buf.writeUInt16LE(16, 34); // bits per sample
20
+ // data chunk
21
+ buf.write('data', 36);
22
+ buf.writeUInt32LE(dataSize, 40);
23
+ for (let i = 0; i < numSamples; i++) {
24
+ const t = i / sampleRate;
25
+ const sample = Math.sin(2 * Math.PI * frequency * t) * 0.1;
26
+ buf.writeInt16LE(Math.round(sample * 32767), 44 + i * 2);
27
+ }
28
+ return buf;
29
+ }
30
+ // ============================================================
31
+ // 调式系统
32
+ // ============================================================
33
+ // 各种调式的音程模式(半音偏移量)
34
+ const SCALES = {
35
+ major: [0, 2, 4, 5, 7, 9, 11],
36
+ minor: [0, 2, 3, 5, 7, 8, 10],
37
+ harmonicMinor: [0, 2, 3, 5, 7, 8, 11],
38
+ melodicMinor: [0, 2, 3, 5, 7, 9, 11],
39
+ pentatonic: [0, 2, 4, 7, 9],
40
+ minorPentatonic: [0, 3, 5, 7, 10],
41
+ blues: [0, 3, 5, 6, 7, 10],
42
+ dorian: [0, 2, 3, 5, 7, 9, 10],
43
+ phrygian: [0, 1, 3, 5, 7, 8, 10],
44
+ lydian: [0, 2, 4, 6, 7, 9, 11],
45
+ mixolydian: [0, 2, 4, 5, 7, 9, 10],
46
+ locrian: [0, 1, 3, 5, 6, 8, 10],
47
+ japanese: [0, 1, 5, 7, 8],
48
+ arabic: [0, 1, 4, 5, 7, 8, 11],
49
+ hungarian: [0, 2, 3, 6, 7, 8, 11],
50
+ wholeTone: [0, 2, 4, 6, 8, 10],
51
+ diminished: [0, 2, 3, 5, 6, 8, 9, 11],
52
+ augmented: [0, 3, 4, 7, 8, 11],
53
+ bebop: [0, 2, 4, 5, 7, 9, 10, 11],
54
+ hirajoshi: [0, 2, 3, 7, 8],
55
+ insen: [0, 1, 5, 7, 10],
56
+ iwato: [0, 1, 5, 6, 10],
57
+ prometheus: [0, 2, 4, 6, 9, 10],
58
+ enigmatic: [0, 1, 4, 6, 8, 10, 11],
59
+ };
60
+ // ============================================================
61
+ // 和弦系统
62
+ // ============================================================
63
+ // 和弦类型(相对于根音的半音偏移)
64
+ const CHORD_TYPES = {
65
+ major: [0, 4, 7],
66
+ minor: [0, 3, 7],
67
+ dim: [0, 3, 6],
68
+ aug: [0, 4, 8],
69
+ sus2: [0, 2, 7],
70
+ sus4: [0, 5, 7],
71
+ maj7: [0, 4, 7, 11],
72
+ min7: [0, 3, 7, 10],
73
+ dom7: [0, 4, 7, 10],
74
+ dim7: [0, 3, 6, 9],
75
+ m7b5: [0, 3, 6, 10],
76
+ add9: [0, 4, 7, 14],
77
+ madd9: [0, 3, 7, 14],
78
+ '6': [0, 4, 7, 9],
79
+ m6: [0, 3, 7, 9],
80
+ '9': [0, 4, 7, 10, 14],
81
+ maj9: [0, 4, 7, 11, 14],
82
+ min9: [0, 3, 7, 10, 14],
83
+ '11': [0, 4, 7, 10, 14, 17],
84
+ '13': [0, 4, 7, 10, 14, 17, 21],
85
+ };
86
+ // 和弦进行模板(罗马数字 -> 调式内音级,0-based)
87
+ const CHORD_PROGRESSIONS = [
88
+ [0, 3, 4, 0], // I-IV-V-I
89
+ [0, 4, 5, 3], // I-V-vi-IV (流行经典)
90
+ [0, 5, 3, 4], // I-vi-IV-V (50s)
91
+ [1, 4, 0, 0], // ii-V-I-I (爵士基本)
92
+ [0, 3, 0, 4], // I-IV-I-V (布鲁斯简化)
93
+ [0, 5, 1, 4], // I-vi-ii-V (循环)
94
+ [0, 2, 3, 4], // I-iii-IV-V
95
+ [5, 3, 0, 4], // vi-IV-I-V
96
+ [0, 6, 3, 4], // I-vii-IV-V
97
+ [0, 3, 5, 4], // I-IV-vi-V
98
+ [1, 4, 0, 5], // ii-V-I-vi
99
+ [0, 0, 3, 3, 4, 4, 0, 0], // 12-bar 简化
100
+ [0, 3, 4, 3], // I-IV-V-IV (摇滚)
101
+ [5, 4, 3, 0], // vi-V-IV-I (逆行)
102
+ [0, 2, 5, 3], // I-iii-vi-IV
103
+ ];
104
+ // ============================================================
105
+ // 节奏系统
106
+ // ============================================================
107
+ const RHYTHM_PATTERNS = [
108
+ [1, 1, 1, 1],
109
+ [2, 1, 1, 2, 1, 1],
110
+ [1, 1, 2, 1, 1, 2],
111
+ [3, 1, 2, 2],
112
+ [1, 2, 1, 2, 1, 1],
113
+ [1, 1, 1, 1, 2, 2],
114
+ [2, 2, 1, 1, 1, 1],
115
+ [1, 3, 1, 3],
116
+ [1, 1, 1, 2, 1, 1, 1, 2],
117
+ [3, 1, 1, 3, 1, 1],
118
+ [1, 1, 2, 2, 1, 1],
119
+ [4, 2, 1, 1],
120
+ [1, 1, 1, 3],
121
+ [2, 3, 2, 1],
122
+ [1, 2, 3, 2],
123
+ ];
124
+ // ============================================================
125
+ // 旋律走向系统
126
+ // ============================================================
127
+ const CONTOURS = [
128
+ 'ascending',
129
+ 'descending',
130
+ 'arch',
131
+ 'valley',
132
+ 'zigzag',
133
+ 'random_walk',
134
+ 'repetitive',
135
+ 'call_response',
136
+ 'spiral', // 螺旋上升
137
+ 'cascade', // 瀑布式下降
138
+ 'pendulum', // 钟摆式
139
+ 'stepwise', // 逐级进行
140
+ ];
141
+ /** 根据波形类型和相位生成采样值 */
142
+ function generateWaveformSample(waveform, phase, posInNote) {
143
+ switch (waveform) {
144
+ case 'sine':
145
+ return Math.sin(phase);
146
+ case 'triangle':
147
+ return (2 / Math.PI) * Math.asin(Math.sin(phase));
148
+ case 'soft_square':
149
+ // 多个正弦波叠加模拟柔和方波
150
+ return Math.sin(phase) * 0.7 + Math.sin(phase * 3) * 0.2 + Math.sin(phase * 5) * 0.07 + Math.sin(phase * 7) * 0.03;
151
+ case 'sawtooth':
152
+ // 锯齿波(多个泛音叠加)
153
+ return Math.sin(phase) * 0.5 + Math.sin(phase * 2) * 0.25 + Math.sin(phase * 3) * 0.167 + Math.sin(phase * 4) * 0.125;
154
+ case 'organ':
155
+ // 管风琴(奇数泛音 + 偶数泛音轻微)
156
+ return Math.sin(phase) * 0.5 + Math.sin(phase * 2) * 0.3 + Math.sin(phase * 3) * 0.15 + Math.sin(phase * 4) * 0.05;
157
+ case 'bell':
158
+ // 钟声(非谐波泛音 + 快速衰减高频)
159
+ return Math.sin(phase) * 0.4 + Math.sin(phase * 2.756) * 0.3 * Math.exp(-posInNote * 3) +
160
+ Math.sin(phase * 4.567) * 0.2 * Math.exp(-posInNote * 5) + Math.sin(phase * 6.234) * 0.1 * Math.exp(-posInNote * 8);
161
+ case 'pluck':
162
+ // 拨弦(高频快速衰减)
163
+ return Math.sin(phase) * 0.5 + Math.sin(phase * 2) * 0.3 * Math.exp(-posInNote * 4) +
164
+ Math.sin(phase * 3) * 0.15 * Math.exp(-posInNote * 6) + Math.sin(phase * 4) * 0.05 * Math.exp(-posInNote * 10);
165
+ case 'pad':
166
+ // 合成垫音(缓慢变化的和声)
167
+ return Math.sin(phase) * 0.4 + Math.sin(phase * 1.01) * 0.3 + Math.sin(phase * 2) * 0.2 + Math.sin(phase * 0.99) * 0.1;
168
+ }
169
+ }
170
+ // ============================================================
171
+ // 核心生成函数
172
+ // ============================================================
173
+ /** 根据 MIDI 音符号计算频率 (A4=69=440Hz) */
174
+ function midiToFreq(midiNote) {
175
+ return 440 * Math.pow(2, (midiNote - 69) / 12);
176
+ }
177
+ /** 获取调式内的和弦类型 */
178
+ function getChordTypeForDegree(degree, scaleType) {
179
+ // 简化的和弦分配逻辑
180
+ if (scaleType.includes('minor') || scaleType === 'dorian' || scaleType === 'phrygian') {
181
+ const minorChords = ['minor', 'dim', 'major', 'minor', 'minor', 'major', 'major'];
182
+ return minorChords[degree % 7];
183
+ }
184
+ const majorChords = ['major', 'minor', 'minor', 'major', 'major', 'minor', 'dim'];
185
+ return majorChords[degree % 7];
186
+ }
187
+ /** 生成旋律音符序列 */
188
+ function generateNoteSequence(noteCount) {
189
+ const scaleNames = Object.keys(SCALES);
190
+ const scale = SCALES[randPick(scaleNames)];
191
+ const contour = randPick([...CONTOURS]);
192
+ // 随机根音 (C3=48 到 C5=72)
193
+ const rootMidi = randInt(48, 72);
194
+ // 构建当前调式在多个八度内的可用音符
195
+ const availableNotes = [];
196
+ for (let octave = -1; octave <= 2; octave++) {
197
+ for (const interval of scale) {
198
+ availableNotes.push(rootMidi + octave * 12 + interval);
199
+ }
200
+ }
201
+ // 过滤到合理范围 (MIDI 36-96, 约 C2-C7)
202
+ const validNotes = availableNotes.filter(n => n >= 36 && n <= 96);
203
+ const notes = [];
204
+ let currentIdx = Math.floor(validNotes.length / 2);
205
+ for (let i = 0; i < noteCount; i++) {
206
+ const progress = i / (noteCount - 1 || 1);
207
+ switch (contour) {
208
+ case 'ascending':
209
+ currentIdx = Math.floor(progress * (validNotes.length - 1));
210
+ break;
211
+ case 'descending':
212
+ currentIdx = Math.floor((1 - progress) * (validNotes.length - 1));
213
+ break;
214
+ case 'arch':
215
+ currentIdx = Math.floor(Math.sin(progress * Math.PI) * (validNotes.length - 1) * 0.4 + validNotes.length * 0.3);
216
+ break;
217
+ case 'valley':
218
+ currentIdx = Math.floor((1 - Math.sin(progress * Math.PI)) * (validNotes.length - 1) * 0.4 + validNotes.length * 0.3);
219
+ break;
220
+ case 'zigzag':
221
+ currentIdx += (i % 2 === 0) ? randInt(1, 3) : -randInt(1, 3);
222
+ break;
223
+ case 'random_walk':
224
+ currentIdx += randInt(-2, 2);
225
+ break;
226
+ case 'repetitive': {
227
+ const motifLen = randInt(3, 5);
228
+ const motifPos = i % motifLen;
229
+ if (i < motifLen) {
230
+ currentIdx += randInt(-2, 2);
231
+ }
232
+ else {
233
+ currentIdx = notes[motifPos] ? validNotes.indexOf(notes[motifPos]) : currentIdx;
234
+ if (currentIdx === -1)
235
+ currentIdx = Math.floor(validNotes.length / 2);
236
+ }
237
+ break;
238
+ }
239
+ case 'call_response': {
240
+ const half = noteCount / 2;
241
+ if (i < half) {
242
+ currentIdx += randInt(0, 2);
243
+ }
244
+ else {
245
+ currentIdx -= randInt(0, 2);
246
+ }
247
+ break;
248
+ }
249
+ case 'spiral':
250
+ // 螺旋:逐渐扩大的振幅
251
+ currentIdx += Math.round(Math.sin(progress * Math.PI * 4) * (1 + progress * 3));
252
+ break;
253
+ case 'cascade':
254
+ // 瀑布:短上行后长下行
255
+ if (i % 5 === 0)
256
+ currentIdx += randInt(2, 4);
257
+ else
258
+ currentIdx -= randInt(0, 1);
259
+ break;
260
+ case 'pendulum':
261
+ // 钟摆:逐渐缩小的振幅
262
+ currentIdx = Math.floor(validNotes.length / 2 + Math.sin(progress * Math.PI * 6) * (validNotes.length * 0.3 * (1 - progress)));
263
+ break;
264
+ case 'stepwise':
265
+ // 逐级:大部分是级进,偶尔跳跃
266
+ if (randInt(1, 10) <= 7) {
267
+ currentIdx += randPick([-1, 1]);
268
+ }
269
+ else {
270
+ currentIdx += randPick([-3, -2, 2, 3]);
271
+ }
272
+ break;
273
+ }
274
+ currentIdx = Math.max(0, Math.min(validNotes.length - 1, currentIdx));
275
+ notes.push(validNotes[currentIdx]);
276
+ }
277
+ return notes;
278
+ }
279
+ /** 生成和弦进行 */
280
+ function generateChordProgression(durationMs, rootMidi, scaleName) {
281
+ const scale = SCALES[scaleName] || SCALES.major;
282
+ const progression = randPick(CHORD_PROGRESSIONS);
283
+ // 每个和弦的时值
284
+ const chordDuration = durationMs / progression.length;
285
+ const durations = progression.map(() => chordDuration);
286
+ const chords = progression.map(degree => {
287
+ // 计算这个音级的根音
288
+ const chordRoot = rootMidi + (scale[degree % scale.length] || 0);
289
+ // 获取和弦类型
290
+ const chordTypeName = getChordTypeForDegree(degree, scaleName);
291
+ const chordIntervals = CHORD_TYPES[chordTypeName];
292
+ // 偶尔使用七和弦/九和弦增加丰富度
293
+ const useExtension = randInt(1, 10) <= 3;
294
+ let intervals = chordIntervals;
295
+ if (useExtension) {
296
+ const extensions = ['maj7', 'min7', 'dom7', 'add9', '6'];
297
+ const extType = randPick(extensions);
298
+ intervals = CHORD_TYPES[extType];
299
+ }
300
+ return intervals.map(interval => chordRoot + interval);
301
+ });
302
+ return { notes: chords, durations };
303
+ }
304
+ /** 写 WAV header */
305
+ function writeWavHeader(buf, dataSize, sampleRate) {
306
+ buf.write('RIFF', 0);
307
+ buf.writeUInt32LE(36 + dataSize, 4);
308
+ buf.write('WAVE', 8);
309
+ buf.write('fmt ', 12);
310
+ buf.writeUInt32LE(16, 16);
311
+ buf.writeUInt16LE(1, 20);
312
+ buf.writeUInt16LE(1, 22);
313
+ buf.writeUInt32LE(sampleRate, 24);
314
+ buf.writeUInt32LE(sampleRate * 2, 28);
315
+ buf.writeUInt16LE(2, 32);
316
+ buf.writeUInt16LE(16, 34);
317
+ buf.write('data', 36);
318
+ buf.writeUInt32LE(dataSize, 40);
319
+ }
320
+ /** 计算 ADSR 包络值 */
321
+ function getEnvelope(posInNote, attack, decay, sustainLevel, release) {
322
+ if (posInNote < attack) {
323
+ return posInNote / attack;
324
+ }
325
+ else if (posInNote < attack + decay) {
326
+ return 1.0 - (1.0 - sustainLevel) * ((posInNote - attack) / decay);
327
+ }
328
+ else if (posInNote < 1.0 - release) {
329
+ return sustainLevel;
330
+ }
331
+ else {
332
+ return sustainLevel * Math.max(0, (1.0 - (posInNote - (1.0 - release)) / release));
333
+ }
334
+ }
335
+ /** 生成随机旋律的 WAV Buffer(带和弦伴奏) */
336
+ export function generateMelodyWav(durationMs, sampleRate = 44100) {
337
+ // 选择调式
338
+ const scaleNames = Object.keys(SCALES);
339
+ const scaleName = randPick(scaleNames);
340
+ const scale = SCALES[scaleName];
341
+ // 随机根音
342
+ const rootMidi = randInt(48, 66);
343
+ // === 旋律部分 ===
344
+ const rhythmPattern = randPick(RHYTHM_PATTERNS);
345
+ const targetNotes = randInt(10, 24);
346
+ const melodyDurations = [];
347
+ while (melodyDurations.length < targetNotes) {
348
+ for (const r of rhythmPattern) {
349
+ melodyDurations.push(r);
350
+ if (melodyDurations.length >= targetNotes)
351
+ break;
352
+ }
353
+ }
354
+ const noteCount = melodyDurations.length;
355
+ const totalMelodyRhythm = melodyDurations.reduce((a, b) => a + b, 0);
356
+ const melodyNoteDurations = melodyDurations.map(d => (d / totalMelodyRhythm) * durationMs);
357
+ const melodyNotes = generateNoteSequence(noteCount);
358
+ // === 和弦部分 ===
359
+ const hasChords = randInt(1, 10) <= 7; // 70% 概率有和弦伴奏
360
+ const chordData = hasChords ? generateChordProgression(durationMs, rootMidi - 12, scaleName) : null;
361
+ // === 音色选择 ===
362
+ const allWaveforms = ['sine', 'triangle', 'soft_square', 'sawtooth', 'organ', 'bell', 'pluck', 'pad'];
363
+ const melodyWaveform = randPick(allWaveforms);
364
+ const chordWaveform = randPick(['pad', 'organ', 'sine', 'triangle']);
365
+ // === 生成音频 ===
366
+ const totalSamples = Math.floor(sampleRate * durationMs / 1000);
367
+ const dataSize = totalSamples * 2;
368
+ const buf = Buffer.alloc(44 + dataSize);
369
+ writeWavHeader(buf, dataSize, sampleRate);
370
+ // 预计算旋律音符时间点
371
+ const melodyStarts = [];
372
+ let accMs = 0;
373
+ for (let n = 0; n < noteCount; n++) {
374
+ melodyStarts.push(Math.floor(sampleRate * accMs / 1000));
375
+ accMs += melodyNoteDurations[n];
376
+ }
377
+ melodyStarts.push(totalSamples);
378
+ // 预计算和弦时间点
379
+ const chordStarts = [];
380
+ if (chordData) {
381
+ let chordAccMs = 0;
382
+ for (let n = 0; n < chordData.notes.length; n++) {
383
+ chordStarts.push(Math.floor(sampleRate * chordAccMs / 1000));
384
+ chordAccMs += chordData.durations[n];
385
+ }
386
+ chordStarts.push(totalSamples);
387
+ }
388
+ // 旋律 ADSR 参数(根据音色不同调整)
389
+ const melodyADSR = melodyWaveform === 'bell' ? { a: 0.01, d: 0.3, s: 0.2, r: 0.4 }
390
+ : melodyWaveform === 'pluck' ? { a: 0.005, d: 0.2, s: 0.3, r: 0.3 }
391
+ : melodyWaveform === 'pad' ? { a: 0.15, d: 0.1, s: 0.8, r: 0.2 }
392
+ : { a: 0.03, d: 0.1, s: 0.7, r: 0.15 };
393
+ for (let i = 0; i < totalSamples; i++) {
394
+ const t = i / sampleRate;
395
+ let sampleValue = 0;
396
+ // --- 旋律层 ---
397
+ let melodyIdx = 0;
398
+ for (let n = 0; n < noteCount; n++) {
399
+ if (i >= melodyStarts[n] && i < melodyStarts[n + 1]) {
400
+ melodyIdx = n;
401
+ break;
402
+ }
403
+ }
404
+ const melodyFreq = midiToFreq(melodyNotes[melodyIdx]);
405
+ const melodyNoteStart = melodyStarts[melodyIdx];
406
+ const melodyNoteEnd = melodyStarts[melodyIdx + 1];
407
+ const melodyNoteLen = melodyNoteEnd - melodyNoteStart;
408
+ const melodyPos = (i - melodyNoteStart) / melodyNoteLen;
409
+ const melodyEnv = getEnvelope(melodyPos, melodyADSR.a, melodyADSR.d, melodyADSR.s, melodyADSR.r);
410
+ const melodyPhase = 2 * Math.PI * melodyFreq * t;
411
+ sampleValue += generateWaveformSample(melodyWaveform, melodyPhase, melodyPos) * melodyEnv * 0.6;
412
+ // --- 和弦层 ---
413
+ if (chordData && chordStarts.length > 1) {
414
+ let chordIdx = 0;
415
+ for (let n = 0; n < chordData.notes.length; n++) {
416
+ if (i >= chordStarts[n] && i < chordStarts[n + 1]) {
417
+ chordIdx = n;
418
+ break;
419
+ }
420
+ }
421
+ const chordNoteStart = chordStarts[chordIdx];
422
+ const chordNoteEnd = chordStarts[chordIdx + 1];
423
+ const chordNoteLen = chordNoteEnd - chordNoteStart;
424
+ const chordPos = (i - chordNoteStart) / chordNoteLen;
425
+ const chordEnv = getEnvelope(chordPos, 0.08, 0.1, 0.6, 0.2);
426
+ const chordNotes = chordData.notes[chordIdx];
427
+ // 叠加和弦中每个音
428
+ for (const note of chordNotes) {
429
+ const chordFreq = midiToFreq(note);
430
+ const chordPhase = 2 * Math.PI * chordFreq * t;
431
+ sampleValue += generateWaveformSample(chordWaveform, chordPhase, chordPos) * chordEnv * (0.25 / chordNotes.length);
432
+ }
433
+ }
434
+ // 总音量控制 + 软削波
435
+ const finalSample = Math.tanh(sampleValue * 0.9) * 0.1;
436
+ buf.writeInt16LE(Math.round(finalSample * 32767), 44 + i * 2);
437
+ }
438
+ return buf;
439
+ }
@@ -0,0 +1,5 @@
1
+ /** 从 URL 获取并转为 base64 */
2
+ export declare function fetchBase64(url: string): Promise<{
3
+ data: string;
4
+ mimeType: string;
5
+ }>;
@@ -0,0 +1,9 @@
1
+ /** 从 URL 获取并转为 base64 */
2
+ export async function fetchBase64(url) {
3
+ const resp = await fetch(url, { redirect: 'follow' });
4
+ if (!resp.ok)
5
+ throw new Error(`Fetch failed: ${resp.status} ${resp.statusText}`);
6
+ const contentType = resp.headers.get('content-type') || 'application/octet-stream';
7
+ const buf = await resp.arrayBuffer();
8
+ return { data: Buffer.from(buf).toString('base64'), mimeType: contentType.split(';')[0].trim() };
9
+ }
@@ -0,0 +1,5 @@
1
+ export * from './assets.js';
2
+ export * from './fetch.js';
3
+ export * from './random.js';
4
+ export * from './annotations.js';
5
+ export * from './audio.js';
@@ -0,0 +1,5 @@
1
+ export * from './assets.js';
2
+ export * from './fetch.js';
3
+ export * from './random.js';
4
+ export * from './annotations.js';
5
+ export * from './audio.js';
@@ -0,0 +1,8 @@
1
+ /** 随机整数 [min, max] */
2
+ export declare function randInt(min: number, max: number): number;
3
+ /** 随机选择数组中的一个元素 */
4
+ export declare function randPick<T>(arr: T[]): T;
5
+ /** 生成随机英文句子 */
6
+ export declare function randSentence(): string;
7
+ /** 生成随机段落 */
8
+ export declare function randParagraph(sentenceCount?: number): string;
@@ -0,0 +1,20 @@
1
+ /** 随机整数 [min, max] */
2
+ export function randInt(min, max) {
3
+ return Math.floor(Math.random() * (max - min + 1)) + min;
4
+ }
5
+ /** 随机选择数组中的一个元素 */
6
+ export function randPick(arr) {
7
+ return arr[Math.floor(Math.random() * arr.length)];
8
+ }
9
+ /** 生成随机英文句子 */
10
+ export function randSentence() {
11
+ const subjects = ['The cat', 'A developer', 'The system', 'An algorithm', 'The server', 'A user', 'The database', 'A function'];
12
+ const verbs = ['processes', 'handles', 'generates', 'transforms', 'validates', 'returns', 'analyzes', 'computes'];
13
+ const objects = ['the request', 'all data points', 'incoming messages', 'binary streams', 'user inputs', 'cached results', 'the configuration', 'error states'];
14
+ const adverbs = ['quickly', 'efficiently', 'randomly', 'recursively', 'asynchronously', 'gracefully', 'silently', 'dynamically'];
15
+ return `${randPick(subjects)} ${randPick(adverbs)} ${randPick(verbs)} ${randPick(objects)}.`;
16
+ }
17
+ /** 生成随机段落 */
18
+ export function randParagraph(sentenceCount = 5) {
19
+ return Array.from({ length: sentenceCount }, () => randSentence()).join(' ');
20
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "mcp-message-test",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for testing message types",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "mcp-message-test": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc && shx chmod +x dist/index.js",
15
+ "prepare": "npm run build",
16
+ "start": "node dist/index.js"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^22",
20
+ "shx": "^0.3.4",
21
+ "typescript": "^5"
22
+ },
23
+ "dependencies": {
24
+ "@byted/modelcontextprotocol-server": "^2.0.0-alpha.2.byted.1",
25
+ "sharp": "^0.34.5",
26
+ "zod": "^4.4.3"
27
+ }
28
+ }