| 1 | # RoslynMcp |
| 2 | |
| 3 | MCP-сервер для помощи агенту при рефакторинге C#: доступ к **Roslyn** (синтаксические деревья, семантика, символы, find usages, rename, code actions). Цель — стабильный тул без глюков (альтернатива Bifrost). |
| 4 | |
| 5 | **Cursor:** примеры `.cursor/rules` для копипаста — **[docs/cursor-rules-examples.md](docs/cursor-rules-examples.md)**. |
| 6 | |
| 7 | **Текущий статус:** работают `roslyn_ping`, `roslyn_get_document_symbols`, `roslyn_get_symbol_at_position`, `roslyn_find_usages`, `roslyn_go_to_definition`, `roslyn_rename`, `roslyn_get_code_actions`, `roslyn_apply_code_action`, `roslyn_get_diagnostics`, `roslyn_get_workspace_navigation_context` (эвристики «связанных файлов» по MSBuild solution; не эквивалентно дереву файлов Cascade IDE — см. описание тула). |
| 8 | |
| 9 | ## MSBuild workspace |
| 10 | |
| 11 | ### Семантическая навигация (`roslyn_get_workspace_navigation_context`) |
| 12 | |
| 13 | Источник файлов — документы, которые поднимает **MSBuildWorkspace** по переданному `.sln`/`.csproj`, а не произвольное дерево из IDE. Встроенные пресеты (`peers_only`, `no_namespace_noise`, `tests_and_peers`, `structure_only`) задаются аргументом `preset` или своими `include_kinds` / `exclude_kinds`. |
| 14 | |
| 15 | ### Свойство `RoslynMcpWorkspace` и блокировка DLL анализаторов при `dotnet build` |
| 16 | |
| 17 | RoslynMcp всегда передаёт в MSBuild свойство **`RoslynMcpWorkspace=true`** вместе с отключением design-only сборки (см. `ServiceLayer/RoslynMcpWorkspaceProperties.cs`). Это **не привязка к конкретному репозиторию**: любой `.csproj` может при желании проверять `$(RoslynMcpWorkspace)` и, например, **не подключать** `ProjectReference` на локальный проект Roslyn-анализатора (`OutputItemType="Analyzer"`), пока workspace открыт через MCP. |
| 18 | |
| 19 | Зачем: процесс RoslynMcp иначе может держать загруженной ту же сборку в `bin\…`, которую `dotnet build` затем перезаписывает — на Windows это даёт «file is being used by another process». Отключение такой ссылки **только при** `RoslynMcpWorkspace=true` оставляет обычные сборки и CI без этого флага с полным набором анализаторов; детали опт-in — в документации **того** решения, где условие добавлено. |
| 20 | |
| 21 | ### Source generators (MVVM Toolkit и др.) |
| 22 | |
| 23 | Для проектов с **source generators** (например `[ObservableProperty]` / `[RelayCommand]` в CommunityToolkit.Mvvm) `roslyn_get_diagnostics` поднимает сгенерированные документы через **`GetSourceGeneratedDocumentsAsync`** и берёт проект и компиляцию из актуального **`workspace.CurrentSolution`** — см. **`ServiceLayer/GetDiagnostics.cs`**. Глобальные свойства MSBuild те же, что для остальных вызовов `MSBuildWorkspace` в этом сервере (**`RoslynMcpWorkspaceProperties`**). |
| 24 | |
| 25 | Раньше без такого конвейёра при зелёном **`dotnet build`** возможны были ложные **CS1061**; в текущей реализации это учтено. Если вывод инструмента всё же расходится со сборкой, ориентир — **`dotnet build`**. Подробнее — **[docs/source-generators-and-diagnostics.md](docs/source-generators-and-diagnostics.md)**. |
| 26 | |
| 27 | ## Требования |
| 28 | |
| 29 | - .NET 10 |
| 30 | |
| 31 | ## Сборка |
| 32 | |
| 33 | ```bash |
| 34 | cd roslyn-mcp |
| 35 | dotnet build |
| 36 | dotnet run |
| 37 | ``` |
| 38 | |
| 39 | Полный перечень имён и описаний (как у инструментов в MCP) — **[docs/MCP-TOOLS.md](docs/MCP-TOOLS.md)**. Тот же источник даёт `mcp-tools.manifest.json` в корне проекта. Обновить оба файла из кода: |
| 40 | |
| 41 | `dotnet run --project tools/ExportMcpManifest -- --write` (рабочий каталог — корень `roslyn-mcp`). |
| 42 | |
| 43 | ## Публикация exe (для MCP в Cursor) |
| 44 | |
| 45 | Чтобы Cursor запускал MCP из **exe**, а не из проекта, сборка не будет блокироваться запущенным процессом. |
| 46 | |
| 47 | ### Быстрый локальный publish (рекомендуется) |
| 48 | |
| 49 | В репозитории есть `publish-and-deploy.ps1`: он прогоняет генерацию `mcp-tools.manifest.json` / `docs/MCP-TOOLS.md`, публикует self-contained `win-x64`, зеркалит в фиксированный путь (по умолчанию `D:\roslyn-mcp`) и гасит процесс, если он лочит файлы. Скрипт проверяет наличие **`BuildHost-netcore\Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll`** рядом с `RoslynMcp.exe` — без этой папки MSBuildWorkspace в Cursor падает с *«build host could not be found»*. |
| 50 | |
| 51 | ```powershell |
| 52 | .\publish-and-deploy.ps1 |
| 53 | # Если в mcp.json указан другой каталог (например D:\RoslynMcp): |
| 54 | .\publish-and-deploy.ps1 -Target "D:\RoslynMcp" |
| 55 | ``` |
| 56 | |
| 57 | ### Релизы (zip + GitLab upload) |
| 58 | |
| 59 | `scripts/publish-release-win.ps1` — это отдельный сценарий: мультиплатформа (win/linux/osx), упаковка в zip и загрузка в GitLab Generic Packages/Release. |
| 60 | |
| 61 | В **csproj** заданы `RuntimeIdentifiers` (в т.ч. `win-x64`) и `SelfContained=true` — самодостаточная сборка (рантайм в папке, не зависит от установленного .NET в системе). Достаточно: |
| 62 | |
| 63 | ```bash |
| 64 | cd roslyn-mcp |
| 65 | dotnet publish -c Release -r win-x64 -o publish |
| 66 | ``` |
| 67 | |
| 68 | Exe появится в `roslyn-mcp/publish/RoslynMcp.exe`. В конфиге MCP в Cursor укажи этот exe (command — полный путь к нему, args — `[]`). |
| 69 | |
| 70 | Обновить exe после правок: снова выполни ту же команду publish, затем перезапусти MCP или Cursor. |
| 71 | |
| 72 | ## VS Code + Claude Code |
| 73 | |
| 74 | Сервер работает по **stdio**. Подключение через [Claude Code](https://code.claude.com/docs/en/mcp) (VS Code с расширением Claude). |
| 75 | |
| 76 | **Требования:** .NET 10 (или собранный exe), установленный [Claude Code CLI](https://docs.anthropic.com/en/docs/build-with-claude/claude-code). |
| 77 | |
| 78 | ### Вариант 1: через exe (рекомендуется) |
| 79 | |
| 80 | Собери exe (см. «Публикация exe» выше), затем добавь MCP: |
| 81 | |
| 82 | ```bash |
| 83 | claude mcp add --transport stdio roslyn -- C:\path\to\roslyn-mcp\publish\RoslynMcp.exe |
| 84 | ``` |
| 85 | |
| 86 | На macOS/Linux подставь свой путь к exe, например `~/projects/roslyn-mcp/publish/RoslynMcp`. |
| 87 | |
| 88 | ### Вариант 2: через dotnet run (из папки проекта) |
| 89 | |
| 90 | Если не собираешь exe, можно запускать из папки репо: |
| 91 | |
| 92 | ```bash |
| 93 | cd /path/to/roslyn-mcp |
| 94 | claude mcp add --transport stdio roslyn -- dotnet run --project . |
| 95 | ``` |
| 96 | |
| 97 | (При первом запуске Claude Code выполнит `dotnet run --project .` в текущей папке — открой проект так, чтобы текущим каталогом была папка `roslyn-mcp`, или укажи полный путь: `dotnet run --project /path/to/roslyn-mcp/RoslynMcp.csproj`.) |
| 98 | |
| 99 | ### Общий конфиг в проекте (.mcp.json) |
| 100 | |
| 101 | Чтобы все в команде использовали один и тот же MCP, положи в корень C#-проекта файл `.mcp.json`: |
| 102 | |
| 103 | ```json |
| 104 | { |
| 105 | "mcpServers": { |
| 106 | "roslyn": { |
| 107 | "type": "stdio", |
| 108 | "command": "C:\\path\\to\\roslyn-mcp\\publish\\RoslynMcp.exe", |
| 109 | "args": [] |
| 110 | } |
| 111 | } |
| 112 | } |
| 113 | ``` |
| 114 | |
| 115 | Путь к `command` каждый подставляет свой (или используй переменные окружения, если Claude Code их поддерживает в `.mcp.json`). После сохранения Claude Code предложит доверять серверу; в чате можно проверить статус командой `/mcp`. |
| 116 | |
| 117 | ## Стиль кода и .editorconfig |
| 118 | |
| 119 | Генераторы кода (конструктор, override, интерфейс, базовый класс) учитывают **.editorconfig** в каталоге файла и выше (ближайший к файлу переопределяет). Учитываются опции в `[*.cs]`: |
| 120 | |
| 121 | - **Типы:** `dotnet_style_predefined_type_for_locals_parameters_members`, `dotnet_style_predefined_type_for_member_access` — ключевые слова C# (`int`, `string`, …) vs .NET (`Int32`, `String`). |
| 122 | - **Отступы:** `indent_style`, `indent_size`, `tab_width` — при вставке в файл используется отступ из файла, при его отсутствии — из .editorconfig. |
| 123 | - **var:** `csharp_style_var_for_built_in_types`, `csharp_style_var_when_type_is_apparent`, `csharp_style_var_elsewhere` — зарезервировано для будущего использования в генерируемом коде. |
| 124 | - **Скобки и переносы:** `csharp_prefer_braces`, `csharp_new_line_before_open_brace` — зарезервировано для форматирования блоков. |
| 125 | |
| 126 | В **Generate constructor** не добавляются backing-поля, если для них уже есть свойство (например `_source` при наличии `Source`). |
| 127 | |
| 128 | **Сам проект RoslynMcp** имеет свой `.editorconfig` в корне: стиль (indent, типы, var, скобки) и **severities анализаторов** (IDE0049, IDE0055, CS0219, naming и т.д. — suggestion/warning/info/hidden/none). Сборка с `EnforceCodeStyleInBuild` и `AnalysisLevel` учитывает эти настройки. |
| 129 | |
| 130 | ## Инструменты (tools) |
| 131 | |
| 132 | | Инструмент | Описание | |
| 133 | |------------|----------| |
| 134 | | `roslyn_ping` | Проверка доступности сервера. | |
| 135 | | `roslyn_get_document_symbols` | Структура C# файла: namespace, class, method, property, field, enum, enum_member, delegate с номерами строк. Параметр: `file_path`. | |
| 136 | | `roslyn_get_symbol_at_position` | Символ в позиции (файл + строка + столбец, 1-based): kind и имя. Опционально `solution_or_project_path` — тогда в ответе Qualified (полное имя). Параметры: `file_path`, `line`, `column`. | |
| 137 | | `roslyn_find_usages` | Все ссылки на символ в solution/project. В выводе: квалифицированное имя (FullyQualifiedFormat), место определения (Definition:), затем список ссылок. Параметры: `solution_or_project_path`, `file_path`, `line`, `column` (на идентификаторе). | |
| 138 | | `roslyn_go_to_definition` | Переход к определению символа: по позиции возвращает file:line:column объявления (для partial — несколько строк). Параметры: `solution_or_project_path`, `file_path`, `line`, `column`. | |
| 139 | | `roslyn_rename` | Переименование символа по solution. Параметры: `solution_or_project_path`, `file_path`, `line`, `column`, `new_name`, опционально `apply` (превью/запись), `rename_in_comments`, `rename_in_strings`, `rename_overloads`, `rename_file` (аналог опций в VS), `rename_partial_type_files` (для топ-уровневого типа — переименовать все `TypeName.cs` / `TypeName.*.cs` в проекте под новое имя типа). | |
| 140 | | `roslyn_get_code_actions` | Список Quick Actions / рефакторингов в позиции (как лампочка в VS). Параметры: `solution_or_project_path`, `file_path`, `line`, `column`. Опционально `end_line`, `end_column` — диапазон выделения для рефакторингов вроде Extract method / Extract local function. Возвращает нумерованный список. | |
| 141 | | `roslyn_apply_code_action` | Применить выбранное code action. Параметры: `solution_or_project_path`, `file_path`, `line`, `column`, `action_index` (0-based). Опционально: `end_line`, `end_column` (тот же диапазон, что при get); `fix_all_scope`: `"document"` \| `"project"` \| `"solution"`; `constant_name` — для Introduce constant; `action_options` — JSON-объект опций для действий с диалогом (Extract interface, Extract base class: имена членов и т.д.). Подробнее: [REFACTORINGS.md](REFACTORINGS.md). | |
| 142 | | `roslyn_get_diagnostics` | Диагностики компиляции и анализаторов по solution/project. Учитывается .editorconfig целевого проекта: исключаются диагностики с `dotnet_diagnostic.<Id>.severity = none` и уровень Hidden от анализаторов. Параметры: `solution_or_project_path`, опционально `file_path`. | |
| 143 | | `roslyn_get_solution_structure` | Список проектов в solution (имя, путь к .csproj). Параметр: `solution_or_project_path` (.sln или .csproj). Для реп с только .slnx: передай путь к главному .csproj — вернётся список подгруженных проектов. | |
| 144 | | `roslyn_generate_interface_from_class` | Сгенерировать C# интерфейс по классу **без диалогов** (обходной путь для Extract Interface). Позиция на класс: `solution_or_project_path`, `file_path`, `line`, `column`. Опционально: `interface_name`, `output_file_path`, `member_names` (массив). Дальше вручную или через code action добавь классу `: IName` и примени «Implement interface». | |
| 145 | | `roslyn_generate_base_class_from_class` | Сгенерировать абстрактный базовый класс по классу **без диалогов** (обходной путь для Extract Base Class). Позиция на класс: `solution_or_project_path`, `file_path`, `line`, `column`. Опционально: `base_class_name`, `output_file_path`, `member_names` (массив). Дальше: добавить классу `: BaseName` и проставить `override` у членов. | |
| 146 | | `roslyn_generate_overrides` | **Generate Overrides** без диалога: по позиции на класс генерирует override виртуальных/абстрактных членов базового типа. Опционально: `member_names` (массив), `insert_into_file` (true — вставить в тело класса). | |
| 147 | | `roslyn_generate_constructor_from_members` | **Generate constructor** без диалога: по позиции на класс генерирует конструктор по instance полям и свойствам с set. Опционально: `member_names`, `insert_into_file`. | |
| 148 | | `roslyn_generate_equals_gethashcode` | **Generate Equals and GetHashCode** без диалога: по позиции на класс генерирует override Equals/GetHashCode по выбранным полям/свойствам. Опционально: `member_names`, `insert_into_file`. | |
| 149 | |
| 150 | ## Лицензия |
| 151 | |
| 152 | MIT License. См. [LICENSE](LICENSE). |
| 153 | |