@@ -31,10 +31,42 @@ pub struct AnalyzeAudioParams {
3131 /// Return format: "full" | "summary" | "visual_only"
3232 #[ serde( default = "default_return_format" ) ]
3333 pub return_format : String ,
34+
35+ /// Maximum number of data points in arrays (for pagination)
36+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
37+ pub max_data_points : Option < usize > ,
38+
39+ /// Pagination cursor for continuing from previous results
40+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
41+ pub cursor : Option < String > ,
42+
43+ /// Include visual data (base64 images) - defaults to false for MCP
44+ #[ serde( default = "default_include_visuals" ) ]
45+ pub include_visuals : bool ,
46+
47+ /// Include raw spectral data arrays
48+ #[ serde( default = "default_include_spectral" ) ]
49+ pub include_spectral : bool ,
50+
51+ /// Include temporal data arrays (beats, onsets)
52+ #[ serde( default = "default_include_temporal" ) ]
53+ pub include_temporal : bool ,
3454}
3555
3656fn default_return_format ( ) -> String {
37- "full" . to_string ( )
57+ "summary" . to_string ( )
58+ }
59+
60+ fn default_include_visuals ( ) -> bool {
61+ false // Never include base64 images by default in MCP
62+ }
63+
64+ fn default_include_spectral ( ) -> bool {
65+ false // Don't include large arrays by default
66+ }
67+
68+ fn default_include_temporal ( ) -> bool {
69+ false // Don't include large arrays by default
3870}
3971
4072#[ derive( Debug , Clone , Serialize , Deserialize ) ]
@@ -120,22 +152,159 @@ impl FerrousWavesMcp {
120152 status. message = Some ( "Analysis complete" . to_string ( ) ) ;
121153 }
122154
155+ // Apply filtering based on parameters
156+ let mut filtered_result = analysis_result;
157+
158+ // Filter visuals unless explicitly requested
159+ if !params. include_visuals {
160+ filtered_result. visuals . waveform = None ;
161+ filtered_result. visuals . spectrogram = None ;
162+ filtered_result. visuals . mel_spectrogram = None ;
163+ filtered_result. visuals . power_curve = None ;
164+ }
165+
166+ // Filter spectral data unless requested
167+ if !params. include_spectral {
168+ filtered_result. spectral . spectral_centroid . clear ( ) ;
169+ filtered_result. spectral . spectral_rolloff . clear ( ) ;
170+ filtered_result. spectral . spectral_flux . clear ( ) ;
171+ filtered_result. spectral . mfcc . clear ( ) ;
172+ filtered_result. spectral . dominant_frequencies . clear ( ) ;
173+ }
174+
175+ // Filter temporal data unless requested
176+ if !params. include_temporal {
177+ filtered_result. temporal . beats . clear ( ) ;
178+ filtered_result. temporal . onsets . clear ( ) ;
179+ }
180+
181+ // Parse cursor for pagination
182+ let offset = if let Some ( cursor) = & params. cursor {
183+ cursor. parse :: < usize > ( ) . unwrap_or ( 0 )
184+ } else {
185+ 0
186+ } ;
187+
188+ // Apply pagination with max_data_points
189+ let mut next_cursor = None ;
190+ if let Some ( max_points) = params. max_data_points {
191+ if params. include_spectral {
192+ // Paginate spectral data
193+ let total_len = filtered_result. spectral . spectral_centroid . len ( ) ;
194+ if offset < total_len {
195+ let end = ( offset + max_points) . min ( total_len) ;
196+ filtered_result. spectral . spectral_centroid =
197+ filtered_result. spectral . spectral_centroid [ offset..end] . to_vec ( ) ;
198+
199+ if end < total_len {
200+ next_cursor = Some ( end. to_string ( ) ) ;
201+ }
202+ }
203+
204+ // Apply same pagination to other spectral arrays
205+ if offset < filtered_result. spectral . spectral_flux . len ( ) {
206+ let end =
207+ ( offset + max_points) . min ( filtered_result. spectral . spectral_flux . len ( ) ) ;
208+ filtered_result. spectral . spectral_flux =
209+ filtered_result. spectral . spectral_flux [ offset..end] . to_vec ( ) ;
210+ }
211+
212+ if offset < filtered_result. spectral . spectral_rolloff . len ( ) {
213+ let end =
214+ ( offset + max_points) . min ( filtered_result. spectral . spectral_rolloff . len ( ) ) ;
215+ filtered_result. spectral . spectral_rolloff =
216+ filtered_result. spectral . spectral_rolloff [ offset..end] . to_vec ( ) ;
217+ }
218+
219+ // MFCC is special - limit number of frames
220+ if offset < filtered_result. spectral . mfcc . len ( ) {
221+ let end = ( offset + max_points) . min ( filtered_result. spectral . mfcc . len ( ) ) ;
222+ filtered_result. spectral . mfcc =
223+ filtered_result. spectral . mfcc [ offset..end] . to_vec ( ) ;
224+ }
225+ }
226+
227+ if params. include_temporal {
228+ // Paginate temporal data
229+ if offset < filtered_result. temporal . beats . len ( ) {
230+ let end = ( offset + max_points) . min ( filtered_result. temporal . beats . len ( ) ) ;
231+ filtered_result. temporal . beats =
232+ filtered_result. temporal . beats [ offset..end] . to_vec ( ) ;
233+
234+ if end < filtered_result. temporal . beats . len ( ) && next_cursor. is_none ( ) {
235+ next_cursor = Some ( end. to_string ( ) ) ;
236+ }
237+ }
238+
239+ if offset < filtered_result. temporal . onsets . len ( ) {
240+ let end = ( offset + max_points) . min ( filtered_result. temporal . onsets . len ( ) ) ;
241+ filtered_result. temporal . onsets =
242+ filtered_result. temporal . onsets [ offset..end] . to_vec ( ) ;
243+
244+ if end < filtered_result. temporal . onsets . len ( ) && next_cursor. is_none ( ) {
245+ next_cursor = Some ( end. to_string ( ) ) ;
246+ }
247+ }
248+ }
249+ }
250+
123251 // Format response based on return_format
124252 let response_data = match params. return_format . as_str ( ) {
125- "summary" => json ! ( {
126- "job_id" : job_id,
127- "status" : "success" ,
128- "summary" : analysis_result. get_summary( ) ,
129- } ) ,
253+ "summary" => {
254+ let mut response = json ! ( {
255+ "job_id" : job_id,
256+ "status" : "success" ,
257+ "summary" : filtered_result. get_summary( ) ,
258+ "insights" : filtered_result. insights. iter( ) . take( 5 ) . cloned( ) . collect:: <Vec <_>>( ) ,
259+ } ) ;
260+ if let Some ( cursor) = next_cursor {
261+ response[ "next_cursor" ] = json ! ( cursor) ;
262+ }
263+ response
264+ }
130265 "visual_only" => json ! ( {
131266 "job_id" : job_id,
132267 "status" : "success" ,
133- "visuals" : analysis_result . get_visuals( ) ,
268+ "visuals" : filtered_result . get_visuals( ) ,
134269 } ) ,
270+ "full" => {
271+ let mut response = json ! ( {
272+ "job_id" : job_id,
273+ "status" : "success" ,
274+ "data" : {
275+ "summary" : filtered_result. summary,
276+ "spectral" : if params. include_spectral {
277+ Some ( filtered_result. spectral)
278+ } else {
279+ None
280+ } ,
281+ "temporal" : if params. include_temporal {
282+ Some ( filtered_result. temporal)
283+ } else {
284+ None
285+ } ,
286+ "visuals" : if params. include_visuals {
287+ Some ( filtered_result. visuals)
288+ } else {
289+ None
290+ } ,
291+ "insights" : filtered_result. insights,
292+ "recommendations" : filtered_result. recommendations,
293+ }
294+ } ) ;
295+ if let Some ( cursor) = next_cursor {
296+ response[ "next_cursor" ] = json ! ( cursor) ;
297+ response[ "has_more" ] = json ! ( true ) ;
298+ } else {
299+ response[ "has_more" ] = json ! ( false ) ;
300+ }
301+ response
302+ }
135303 _ => json ! ( {
136304 "job_id" : job_id,
137305 "status" : "success" ,
138- "data" : analysis_result,
306+ "summary" : filtered_result. get_summary( ) ,
307+ "insights" : filtered_result. insights. iter( ) . take( 5 ) . cloned( ) . collect:: <Vec <_>>( ) ,
139308 } ) ,
140309 } ;
141310
@@ -251,8 +420,32 @@ impl ServerHandler for FerrousWavesMcp {
251420 "return_format" : {
252421 "type" : "string" ,
253422 "enum" : [ "full" , "summary" , "visual_only" ] ,
254- "description" : "Return format" ,
255- "default" : "full"
423+ "description" : "Return format (default: summary for MCP compatibility)" ,
424+ "default" : "summary"
425+ } ,
426+ "max_data_points" : {
427+ "type" : "integer" ,
428+ "description" : "Maximum number of data points in arrays (for pagination)" ,
429+ "default" : 1000
430+ } ,
431+ "cursor" : {
432+ "type" : "string" ,
433+ "description" : "Pagination cursor from previous response's next_cursor field"
434+ } ,
435+ "include_visuals" : {
436+ "type" : "boolean" ,
437+ "description" : "Include visual data (base64 images) - WARNING: very large" ,
438+ "default" : false
439+ } ,
440+ "include_spectral" : {
441+ "type" : "boolean" ,
442+ "description" : "Include raw spectral data arrays" ,
443+ "default" : false
444+ } ,
445+ "include_temporal" : {
446+ "type" : "boolean" ,
447+ "description" : "Include temporal data arrays (beats, onsets)" ,
448+ "default" : false
256449 }
257450 } ,
258451 "required" : [ "file_path" ]
0 commit comments