Reubencf commited on
Commit
eb54b0f
·
1 Parent(s): b15ad2e

fixing some things before video

Browse files
Files changed (2) hide show
  1. mcp-server.js +167 -10
  2. test-mcp-changes.js +210 -0
mcp-server.js CHANGED
@@ -193,10 +193,6 @@ class ReubenOSMCPServer {
193
  type: 'string',
194
  description: 'Song lyrics (will be used to generate music)',
195
  },
196
- passkey: {
197
- type: 'string',
198
- description: 'Your passkey for authentication',
199
- },
200
  },
201
  required: ['title', 'style', 'lyrics'],
202
  },
@@ -215,12 +211,26 @@ class ReubenOSMCPServer {
215
  type: 'string',
216
  description: 'Story content/text to be narrated (max 2000 characters for best performance)',
217
  },
 
 
 
 
 
 
 
 
 
 
218
  passkey: {
219
  type: 'string',
220
- description: 'Your passkey for authentication',
 
 
 
 
221
  },
222
  },
223
- required: ['title', 'content'],
224
  },
225
  },
226
  ],
@@ -245,6 +255,8 @@ class ReubenOSMCPServer {
245
  return await this.generateSongAudio(args);
246
  case 'generate_story_audio':
247
  return await this.generateStoryAudio(args);
 
 
248
  default:
249
  throw new Error(`Unknown tool: ${name}`);
250
  }
@@ -377,6 +389,19 @@ class ReubenOSMCPServer {
377
  };
378
  }
379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  const response = await fetch(API_ENDPOINT, {
381
  method: 'POST',
382
  headers: { 'Content-Type': 'application/json' },
@@ -384,7 +409,7 @@ class ReubenOSMCPServer {
384
  passkey,
385
  action: 'save_file',
386
  fileName,
387
- content,
388
  isPublic,
389
  }),
390
  });
@@ -393,12 +418,13 @@ class ReubenOSMCPServer {
393
 
394
  if (response.ok && data.success) {
395
  const location = isPublic ? 'Public Folder' : `Secure Data (passkey: ${passkey})`;
 
396
 
397
  return {
398
  content: [
399
  {
400
  type: 'text',
401
- text: `✅ File saved: ${fileName}\n📁 Saved to: ${location}`,
402
  },
403
  ],
404
  };
@@ -575,7 +601,9 @@ class ReubenOSMCPServer {
575
 
576
  async generateSongAudio(args) {
577
  try {
578
- const { title, style, lyrics, passkey = 'demo' } = args;
 
 
579
 
580
  if (!title || !style || !lyrics) {
581
  return {
@@ -624,7 +652,9 @@ class ReubenOSMCPServer {
624
 
625
  async generateStoryAudio(args) {
626
  try {
627
- const { title, content, passkey = 'demo' } = args;
 
 
628
 
629
  if (!title || !content) {
630
  return {
@@ -671,6 +701,133 @@ class ReubenOSMCPServer {
671
  }
672
  }
673
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674
  async run() {
675
  const transport = new StdioServerTransport();
676
  await this.server.connect(transport);
 
193
  type: 'string',
194
  description: 'Song lyrics (will be used to generate music)',
195
  },
 
 
 
 
196
  },
197
  required: ['title', 'style', 'lyrics'],
198
  },
 
211
  type: 'string',
212
  description: 'Story content/text to be narrated (max 2000 characters for best performance)',
213
  },
214
+ },
215
+ required: ['title', 'content'],
216
+ },
217
+ },
218
+ {
219
+ name: 'analyze_quiz',
220
+ description: 'Analyze quiz answers from quiz_answers.json against the quiz.json questions and provide feedback on correctness',
221
+ inputSchema: {
222
+ type: 'object',
223
+ properties: {
224
  passkey: {
225
  type: 'string',
226
+ description: 'Your passkey for accessing the quiz files',
227
+ },
228
+ isPublic: {
229
+ type: 'boolean',
230
+ description: 'Set to true if quiz files are in public folder. Default: false',
231
  },
232
  },
233
+ required: ['passkey'],
234
  },
235
  },
236
  ],
 
255
  return await this.generateSongAudio(args);
256
  case 'generate_story_audio':
257
  return await this.generateStoryAudio(args);
258
+ case 'analyze_quiz':
259
+ return await this.analyzeQuiz(args);
260
  default:
261
  throw new Error(`Unknown tool: ${name}`);
262
  }
 
389
  };
390
  }
391
 
392
+ // Ensure content is properly stringified if it's an object
393
+ let fileContent = content;
394
+ if (typeof content === 'object' && content !== null) {
395
+ fileContent = JSON.stringify(content, null, 2);
396
+ }
397
+
398
+ // Handle special characters in content for code files
399
+ const ext = fileName.split('.').pop().toLowerCase();
400
+ const isCodeFile = ['dart', 'js', 'ts', 'jsx', 'tsx', 'py', 'java', 'cpp', 'c', 'h', 'hpp', 'cs', 'swift', 'kt', 'go', 'rs', 'rb', 'php'].includes(ext);
401
+
402
+ // Ensure content is a string
403
+ fileContent = String(fileContent);
404
+
405
  const response = await fetch(API_ENDPOINT, {
406
  method: 'POST',
407
  headers: { 'Content-Type': 'application/json' },
 
409
  passkey,
410
  action: 'save_file',
411
  fileName,
412
+ content: fileContent,
413
  isPublic,
414
  }),
415
  });
 
418
 
419
  if (response.ok && data.success) {
420
  const location = isPublic ? 'Public Folder' : `Secure Data (passkey: ${passkey})`;
421
+ const fileType = isCodeFile ? `(${ext.toUpperCase()} code file)` : '';
422
 
423
  return {
424
  content: [
425
  {
426
  type: 'text',
427
+ text: `✅ File saved: ${fileName} ${fileType}\n📁 Saved to: ${location}\n📊 Size: ${(new TextEncoder().encode(fileContent).length / 1024).toFixed(2)} KB`,
428
  },
429
  ],
430
  };
 
601
 
602
  async generateSongAudio(args) {
603
  try {
604
+ const { title, style, lyrics } = args;
605
+ // Use a default passkey internally - users don't need to provide it
606
+ const passkey = 'voice_default';
607
 
608
  if (!title || !style || !lyrics) {
609
  return {
 
652
 
653
  async generateStoryAudio(args) {
654
  try {
655
+ const { title, content } = args;
656
+ // Use a default passkey internally - users don't need to provide it
657
+ const passkey = 'voice_default';
658
 
659
  if (!title || !content) {
660
  return {
 
701
  }
702
  }
703
 
704
+ async analyzeQuiz(args) {
705
+ try {
706
+ const { passkey, isPublic = false } = args;
707
+
708
+ if (!passkey) {
709
+ return {
710
+ content: [{ type: 'text', text: '❌ Passkey is required to access quiz files' }],
711
+ };
712
+ }
713
+
714
+ // Read quiz.json
715
+ const quizUrl = new URL(API_ENDPOINT);
716
+ quizUrl.searchParams.set('passkey', passkey);
717
+ if (isPublic) quizUrl.searchParams.set('isPublic', 'true');
718
+
719
+ const quizResponse = await fetch(quizUrl, {
720
+ method: 'GET',
721
+ headers: { 'Content-Type': 'application/json' },
722
+ });
723
+
724
+ const quizData = await quizResponse.json();
725
+
726
+ if (!quizResponse.ok || !quizData.success) {
727
+ return {
728
+ content: [{ type: 'text', text: `❌ Failed to read quiz.json: ${quizData.error || 'Unknown error'}` }],
729
+ };
730
+ }
731
+
732
+ const quizFile = quizData.files.find(f => f.name === 'quiz.json');
733
+ if (!quizFile) {
734
+ return {
735
+ content: [{ type: 'text', text: '❌ quiz.json not found in storage' }],
736
+ };
737
+ }
738
+
739
+ // Read quiz_answers.json
740
+ const answersFile = quizData.files.find(f => f.name === 'quiz_answers.json');
741
+ if (!answersFile) {
742
+ return {
743
+ content: [{ type: 'text', text: '❌ quiz_answers.json not found in storage' }],
744
+ };
745
+ }
746
+
747
+ // Parse the quiz and answers
748
+ const quiz = JSON.parse(quizFile.content);
749
+ const answers = JSON.parse(answersFile.content);
750
+
751
+ // Analyze the answers
752
+ let correctCount = 0;
753
+ let totalPoints = 0;
754
+ let maxPoints = 0;
755
+ const feedback = [];
756
+
757
+ quiz.questions.forEach((question, index) => {
758
+ const userAnswer = answers.answers?.[question.id] || answers[question.id];
759
+ const points = question.points || 1;
760
+ maxPoints += points;
761
+
762
+ // For multiple choice questions, check if answer matches any correct option
763
+ if (question.type === 'multiple_choice') {
764
+ const correctAnswer = question.correctAnswer || question.correct;
765
+ const isCorrect = userAnswer === correctAnswer ||
766
+ (Array.isArray(correctAnswer) && correctAnswer.includes(userAnswer));
767
+
768
+ if (isCorrect) {
769
+ correctCount++;
770
+ totalPoints += points;
771
+ feedback.push(`✅ Question ${index + 1} (${question.id}): Correct! (+${points} points)`);
772
+ } else {
773
+ feedback.push(`❌ Question ${index + 1} (${question.id}): Incorrect. Your answer: "${userAnswer}"${question.explanation ? `. ${question.explanation}` : ''}`);
774
+ }
775
+ } else if (question.type === 'true_false') {
776
+ const correctAnswer = question.correctAnswer || question.correct;
777
+ const isCorrect = String(userAnswer).toLowerCase() === String(correctAnswer).toLowerCase();
778
+
779
+ if (isCorrect) {
780
+ correctCount++;
781
+ totalPoints += points;
782
+ feedback.push(`✅ Question ${index + 1} (${question.id}): Correct! (+${points} points)`);
783
+ } else {
784
+ feedback.push(`❌ Question ${index + 1} (${question.id}): Incorrect. Your answer: "${userAnswer}"${question.explanation ? `. ${question.explanation}` : ''}`);
785
+ }
786
+ } else {
787
+ // For other question types, do a simple string comparison
788
+ const correctAnswer = question.correctAnswer || question.answer || question.correct;
789
+ const isCorrect = userAnswer === correctAnswer;
790
+
791
+ if (isCorrect) {
792
+ correctCount++;
793
+ totalPoints += points;
794
+ feedback.push(`✅ Question ${index + 1} (${question.id}): Correct! (+${points} points)`);
795
+ } else {
796
+ feedback.push(`❌ Question ${index + 1} (${question.id}): Your answer: "${userAnswer}"${question.explanation ? `. ${question.explanation}` : ''}`);
797
+ }
798
+ }
799
+ });
800
+
801
+ const percentage = Math.round((totalPoints / maxPoints) * 100);
802
+ const grade = percentage >= 90 ? 'A' :
803
+ percentage >= 80 ? 'B' :
804
+ percentage >= 70 ? 'C' :
805
+ percentage >= 60 ? 'D' : 'F';
806
+
807
+ return {
808
+ content: [
809
+ {
810
+ type: 'text',
811
+ text: `📊 Quiz Analysis Results for "${quiz.title}"
812
+
813
+ 📈 Score: ${totalPoints}/${maxPoints} points (${percentage}%)
814
+ ✅ Correct Answers: ${correctCount}/${quiz.questions.length}
815
+ 🎯 Grade: ${grade}
816
+
817
+ 📝 Question Feedback:
818
+ ${feedback.join('\n')}
819
+
820
+ ${percentage >= 70 ? '🎉 Great job!' : '📚 Keep studying and try again!'}`,
821
+ },
822
+ ],
823
+ };
824
+ } catch (error) {
825
+ return {
826
+ content: [{ type: 'text', text: `❌ Error analyzing quiz: ${error.message}` }],
827
+ };
828
+ }
829
+ }
830
+
831
  async run() {
832
  const transport = new StdioServerTransport();
833
  await this.server.connect(transport);
test-mcp-changes.js ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+ // Test script for MCP server changes
3
+
4
+ import fetch from 'node-fetch';
5
+
6
+ const BASE_URL = process.env.REUBENOS_URL || 'http://localhost:3000';
7
+ const API_ENDPOINT = `${BASE_URL}/api/mcp-handler`;
8
+
9
+ async function testFlutterCodeSave() {
10
+ console.log('Testing Flutter code save...');
11
+
12
+ const flutterCode = `import 'package:flutter/material.dart';
13
+
14
+ void main() {
15
+ runApp(MyApp());
16
+ }
17
+
18
+ class MyApp extends StatelessWidget {
19
+ @override
20
+ Widget build(BuildContext context) {
21
+ return MaterialApp(
22
+ title: 'Flutter Demo',
23
+ theme: ThemeData(
24
+ primarySwatch: Colors.blue,
25
+ ),
26
+ home: MyHomePage(title: 'Flutter Demo Home Page'),
27
+ );
28
+ }
29
+ }
30
+
31
+ class MyHomePage extends StatefulWidget {
32
+ MyHomePage({Key? key, required this.title}) : super(key: key);
33
+ final String title;
34
+
35
+ @override
36
+ _MyHomePageState createState() => _MyHomePageState();
37
+ }
38
+
39
+ class _MyHomePageState extends State<MyHomePage> {
40
+ int _counter = 0;
41
+
42
+ void _incrementCounter() {
43
+ setState(() {
44
+ _counter++;
45
+ });
46
+ }
47
+
48
+ @override
49
+ Widget build(BuildContext context) {
50
+ return Scaffold(
51
+ appBar: AppBar(
52
+ title: Text(widget.title),
53
+ ),
54
+ body: Center(
55
+ child: Column(
56
+ mainAxisAlignment: MainAxisAlignment.center,
57
+ children: <Widget>[
58
+ Text(
59
+ 'You have pushed the button this many times:',
60
+ ),
61
+ Text(
62
+ '$_counter',
63
+ style: Theme.of(context).textTheme.headline4,
64
+ ),
65
+ ],
66
+ ),
67
+ ),
68
+ floatingActionButton: FloatingActionButton(
69
+ onPressed: _incrementCounter,
70
+ tooltip: 'Increment',
71
+ child: Icon(Icons.add),
72
+ ),
73
+ );
74
+ }
75
+ }`;
76
+
77
+ try {
78
+ const response = await fetch(API_ENDPOINT, {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify({
82
+ passkey: 'test123',
83
+ action: 'save_file',
84
+ fileName: 'main.dart',
85
+ content: flutterCode,
86
+ isPublic: false,
87
+ }),
88
+ });
89
+
90
+ const data = await response.json();
91
+ console.log('Flutter code save result:', data);
92
+
93
+ if (data.success) {
94
+ console.log('✅ Flutter code saved successfully!');
95
+
96
+ // Try to read it back
97
+ const readUrl = new URL(API_ENDPOINT);
98
+ readUrl.searchParams.set('passkey', 'test123');
99
+
100
+ const readResponse = await fetch(readUrl, {
101
+ method: 'GET',
102
+ headers: { 'Content-Type': 'application/json' },
103
+ });
104
+
105
+ const readData = await readResponse.json();
106
+ if (readData.success) {
107
+ const file = readData.files.find(f => f.name === 'main.dart');
108
+ if (file && file.content === flutterCode) {
109
+ console.log('✅ Flutter code verified - content matches!');
110
+ } else {
111
+ console.log('⚠️ Flutter code content mismatch or file not found');
112
+ }
113
+ }
114
+ } else {
115
+ console.log('❌ Failed to save Flutter code:', data.error);
116
+ }
117
+ } catch (error) {
118
+ console.error('Error testing Flutter code save:', error);
119
+ }
120
+ }
121
+
122
+ async function testAnalyzeQuiz() {
123
+ console.log('\nTesting analyze quiz functionality...');
124
+
125
+ // First, create a sample quiz
126
+ const quiz = {
127
+ title: "Sample Quiz",
128
+ description: "Test quiz for analysis",
129
+ questions: [
130
+ {
131
+ id: "q1",
132
+ type: "multiple_choice",
133
+ question: "What is 2+2?",
134
+ options: ["3", "4", "5", "6"],
135
+ correctAnswer: "4",
136
+ points: 1
137
+ },
138
+ {
139
+ id: "q2",
140
+ type: "multiple_choice",
141
+ question: "What is the capital of France?",
142
+ options: ["London", "Berlin", "Paris", "Madrid"],
143
+ correctAnswer: "Paris",
144
+ points: 1
145
+ },
146
+ {
147
+ id: "q3",
148
+ type: "true_false",
149
+ question: "The Earth is flat",
150
+ correctAnswer: false,
151
+ points: 1
152
+ }
153
+ ]
154
+ };
155
+
156
+ const quizAnswers = {
157
+ answers: {
158
+ "q1": "4",
159
+ "q2": "London",
160
+ "q3": false
161
+ }
162
+ };
163
+
164
+ try {
165
+ // Save quiz.json
166
+ await fetch(API_ENDPOINT, {
167
+ method: 'POST',
168
+ headers: { 'Content-Type': 'application/json' },
169
+ body: JSON.stringify({
170
+ passkey: 'test123',
171
+ action: 'save_file',
172
+ fileName: 'quiz.json',
173
+ content: JSON.stringify(quiz, null, 2),
174
+ isPublic: false,
175
+ }),
176
+ });
177
+
178
+ // Save quiz_answers.json
179
+ await fetch(API_ENDPOINT, {
180
+ method: 'POST',
181
+ headers: { 'Content-Type': 'application/json' },
182
+ body: JSON.stringify({
183
+ passkey: 'test123',
184
+ action: 'save_file',
185
+ fileName: 'quiz_answers.json',
186
+ content: JSON.stringify(quizAnswers, null, 2),
187
+ isPublic: false,
188
+ }),
189
+ });
190
+
191
+ console.log('✅ Quiz and answers saved. Ready to test analyze function.');
192
+ console.log('Note: The analyze_quiz function needs to be called through the MCP server.');
193
+ console.log('Expected results: 2/3 correct (q1 ✅, q2 ❌, q3 ✅)');
194
+ } catch (error) {
195
+ console.error('Error setting up quiz test:', error);
196
+ }
197
+ }
198
+
199
+ // Run tests
200
+ console.log('Starting MCP server tests...');
201
+ console.log('Using server:', BASE_URL);
202
+
203
+ testFlutterCodeSave().then(() => {
204
+ return testAnalyzeQuiz();
205
+ }).then(() => {
206
+ console.log('\n✅ All tests completed!');
207
+ console.log('Note: To fully test the analyze_quiz function, use the MCP client with the analyze_quiz tool.');
208
+ }).catch(error => {
209
+ console.error('Test failed:', error);
210
+ });