| 1 | using System.Text.Json; |
| 2 | using ModelContextProtocol.Protocol; |
| 3 | using Tool = ModelContextProtocol.Protocol.Tool; |
| 4 | |
| 5 | namespace WebcamAnalysisMcp; |
| 6 | |
| 7 | /// <summary>Каталог MCP-тулов. Согласован с <c>mcp-tools.manifest.json</c> и <c>docs/MCP-TOOLS.md</c> (генерация: <c>tools/ExportMcpManifest</c>).</summary> |
| 8 | internal static class ToolCatalog |
| 9 | { |
| 10 | private static JsonElement Schema(object schema) => JsonSerializer.SerializeToElement(schema); |
| 11 | |
| 12 | internal static List<Tool> Build() => |
| 13 | [ |
| 14 | new() |
| 15 | { |
| 16 | Name = "analyze_burst_sequence", |
| 17 | Description = "Проанализировать серию кадров (burst) как квазии-видео: оценить динамику движения, выделить пики/сцены и вернуть структурированный отчёт.", |
| 18 | InputSchema = Schema(new |
| 19 | { |
| 20 | type = "object", |
| 21 | properties = new |
| 22 | { |
| 23 | workspace_path = new { type = "string", description = "Каталог workspace (корень проекта в Cursor)." }, |
| 24 | burst_dir = new { type = "string", description = "Путь к папке burst (абсолютный или относительный к workspace)." }, |
| 25 | sample_every = new { type = "integer", description = "Брать каждый N-й кадр для анализа (по умолчанию 1)." }, |
| 26 | max_frames = new { type = "integer", description = "Максимум кадров для анализа (по умолчанию 3000)." }, |
| 27 | scene_cut_threshold = new { type = "number", description = "Порог резкой смены сцены по шкале 0..255 (по умолчанию 35)." } |
| 28 | }, |
| 29 | required = new[] { "workspace_path", "burst_dir" } |
| 30 | }) |
| 31 | }, |
| 32 | new() |
| 33 | { |
| 34 | Name = "pdf_capture_burst", |
| 35 | Description = "Сделать серию изображений страниц PDF. Сохраняет страницы как картинки в подпапку внутри workspace и возвращает JSON с путём и параметрами страниц.", |
| 36 | InputSchema = Schema(new |
| 37 | { |
| 38 | type = "object", |
| 39 | properties = new |
| 40 | { |
| 41 | workspace_path = new { type = "string", description = "Каталог workspace (корень проекта в Cursor)." }, |
| 42 | pdf_path = new { type = "string", description = "Путь к PDF-файлу (абсолютный или относительный к workspace)." }, |
| 43 | from_page = new { type = "integer", description = "Номер первой страницы (1-based, по умолчанию 1)." }, |
| 44 | to_page = new { type = "integer", description = "Номер последней страницы (1-based, по умолчанию конец файла)." }, |
| 45 | dpi = new { type = "integer", description = "Плотность рендера в DPI (по умолчанию 200)." }, |
| 46 | image_format = new { type = "string", description = "Формат кадров: jpg или png (по умолчанию jpg)." }, |
| 47 | jpeg_quality = new { type = "integer", description = "Качество JPEG 1..100 (по умолчанию 92)." }, |
| 48 | output_subdir = new { type = "string", description = @"Подкаталог внутри workspace для сохранения страниц (по умолчанию .cascade-ide\pdf-captures)." }, |
| 49 | burst_name = new { type = "string", description = "Имя серии (опционально, по умолчанию по имени файла)." } |
| 50 | }, |
| 51 | required = new[] { "workspace_path", "pdf_path" } |
| 52 | }) |
| 53 | }, |
| 54 | new() |
| 55 | { |
| 56 | Name = "ocr_image_batch", |
| 57 | Description = "Сделать OCR для серии изображений (страницы PDF или burst) через внешний tesseract и вернуть JSON по страницам.", |
| 58 | InputSchema = Schema(new |
| 59 | { |
| 60 | type = "object", |
| 61 | properties = new |
| 62 | { |
| 63 | workspace_path = new { type = "string", description = "Каталог workspace (корень проекта в Cursor)." }, |
| 64 | images_dir = new { type = "string", description = "Путь к папке с изображениями (абсолютный или относительный к workspace)." }, |
| 65 | lang = new { type = "string", description = "Коды языков tesseract, например \"eng+rus\" (по умолчанию eng)." }, |
| 66 | sample_every = new { type = "integer", description = "Брать каждый N-й файл (по умолчанию 1)." }, |
| 67 | max_images = new { type = "integer", description = "Максимум изображений для OCR (по умолчанию 1000)." }, |
| 68 | output_json_path = new { type = "string", description = "Путь для сохранения JSON (относительно workspace). По умолчанию ocr.json в images_dir." } |
| 69 | }, |
| 70 | required = new[] { "workspace_path", "images_dir" } |
| 71 | }) |
| 72 | }, |
| 73 | new() |
| 74 | { |
| 75 | Name = "analyze_audio_sequence", |
| 76 | Description = "Проанализировать WAV-файл: громкость, пики, долю тишины, активность речи/звука и таймлайн по окнам.", |
| 77 | InputSchema = Schema(new |
| 78 | { |
| 79 | type = "object", |
| 80 | properties = new |
| 81 | { |
| 82 | workspace_path = new { type = "string", description = "Каталог workspace (корень проекта в Cursor)." }, |
| 83 | audio_path = new { type = "string", description = "Путь к WAV-файлу (абсолютный или относительный к workspace)." }, |
| 84 | frame_ms = new { type = "integer", description = "Размер окна анализа в мс (по умолчанию 50)." }, |
| 85 | silence_threshold_db = new { type = "number", description = "Порог тишины в dBFS (по умолчанию -45)." } |
| 86 | }, |
| 87 | required = new[] { "workspace_path", "audio_path" } |
| 88 | }) |
| 89 | }, |
| 90 | new() |
| 91 | { |
| 92 | Name = "transcribe_audio_whisper", |
| 93 | Description = "Локальная транскрипция аудио через Whisper.net (whisper.cpp runtime). Поддерживаются WAV и WebM/MP4/M4A (через FFmpeg в PATH). Возвращает распознанный текст и сегменты с таймкодами.", |
| 94 | InputSchema = Schema(new |
| 95 | { |
| 96 | type = "object", |
| 97 | properties = new |
| 98 | { |
| 99 | workspace_path = new { type = "string", description = "Каталог workspace (корень проекта в Cursor)." }, |
| 100 | audio_path = new { type = "string", description = "Путь к аудиофайлу (абсолютный или относительный к workspace)." }, |
| 101 | model_path = new { type = "string", description = "Путь к ggml/gguf-модели Whisper. Если не задан — берётся из переменной окружения WHISPER_MODEL_PATH." }, |
| 102 | language = new { type = "string", description = "Язык (например ru, en, auto). По умолчанию auto." }, |
| 103 | max_segments = new { type = "integer", description = "Ограничение количества сегментов в ответе (по умолчанию 1000)." } |
| 104 | }, |
| 105 | required = new[] { "workspace_path", "audio_path" } |
| 106 | }) |
| 107 | }, |
| 108 | new() |
| 109 | { |
| 110 | Name = "analyze_av_sequence", |
| 111 | Description = "Комплексный анализ A/V-сессии: объединяет динамику движения из кадров и аудио-активность в одном отчёте.", |
| 112 | InputSchema = Schema(new |
| 113 | { |
| 114 | type = "object", |
| 115 | properties = new |
| 116 | { |
| 117 | workspace_path = new { type = "string", description = "Каталог workspace (корень проекта в Cursor)." }, |
| 118 | session_dir = new { type = "string", description = "Путь к директории A/V-сессии (абсолютный или относительный к workspace)." }, |
| 119 | sample_every = new { type = "integer", description = "Видео-анализ: каждый N-й кадр (по умолчанию 1)." }, |
| 120 | max_frames = new { type = "integer", description = "Видео-анализ: максимум кадров (по умолчанию 3000)." }, |
| 121 | scene_cut_threshold = new { type = "number", description = "Видео-анализ: порог scene cut (по умолчанию 35)." }, |
| 122 | audio_frame_ms = new { type = "integer", description = "Аудио-анализ: окно в мс (по умолчанию 50)." }, |
| 123 | silence_threshold_db = new { type = "number", description = "Аудио-анализ: порог тишины dBFS (по умолчанию -45)." } |
| 124 | }, |
| 125 | required = new[] { "workspace_path", "session_dir" } |
| 126 | }) |
| 127 | } |
| 128 | ]; |
| 129 | } |