206 lines
17 KiB
C#
206 lines
17 KiB
C#
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
using ModelContextProtocol.Protocol;
|
|
using ModelContextProtocol.Server;
|
|
using Sozsoft.Mcp.Dtos;
|
|
using Sozsoft.Platform.DeveloperKit;
|
|
using Sozsoft.Platform.ListForms;
|
|
|
|
namespace Sozsoft.Mcp.Tools;
|
|
|
|
/// <summary>
|
|
/// Ekran (wizard) ve custom component gelistirme araclari. Uretim uygulamadaki Wizard ve
|
|
/// Custom Components servislerinden gecer; menu, yetki, dil anahtari ve seed dosyasi ekrandaki
|
|
/// yolla ayni sekilde olusur.
|
|
/// </summary>
|
|
[McpServerToolType]
|
|
public sealed class McpDevelopTools(IMcpDevelopAppService developAppService)
|
|
{
|
|
private static readonly JsonSerializerOptions TextOptions = new(JsonSerializerDefaults.Web);
|
|
|
|
[McpServerTool(Name = McpToolNames.GetDevGuide, ReadOnly = true)]
|
|
[Description("Ekran ve bilesen uretim sozlesmesini okur: wizard alanlari, enum degerleri, editor/lookup/script sozlesmesi, custom component kurallari. section bos ise baslik listesi doner; once listeyi al, sonra ihtiyac duydugun bolumu numarasiyla iste (ornegin 4.2). Rehberde dosya yazmak anlatilan yerlerde dosya yerine bu sunucunun araclarini kullan.")]
|
|
public async Task<McpDevGuideDto> GetDevGuideAsync(
|
|
[Description("'lowcode' (uretim protokolu ve artefakt semalari, varsayilan) ya da 'reference' (JSON kolon sozlesmeleri, tasarimci envanteri).")]
|
|
string? document = null,
|
|
[Description("Bolum numarasi (4.2) ya da baslik metninin bir parcasi. Bos birakilirsa baslik listesi doner.")]
|
|
string? section = null)
|
|
=> await developAppService.GetDevGuideAsync(document, section);
|
|
|
|
[McpServerTool(Name = McpToolNames.CreateTable, Destructive = false, Idempotent = false)]
|
|
[Description("Ekranin baglanacagi yeni tabloyu SQL Query Manager tablo tasarimcisiyla ayni bicimde olusturur ve script'i seed dosyasina (sql/object) yazar. Id (uniqueidentifier, PK), TenantId ve tam audit kolonlari (CreationTime, CreatorId, LastModificationTime, LastModifierId, IsDeleted, DeletionTime, DeleterId) otomatik eklenir; columns icine yalnizca is kolonlarini yaz. Yalnizca yeni tablo olusturur: tablo varsa reddeder, kolon ekleme/degistirme tablo tasarimcisindan yapilir. SQL Server veri kaynaginda calisir. Ardindan deploy_wizard ile ekrani kur.")]
|
|
public async Task<McpCreateTableResultDto> CreateTableAsync(
|
|
[Description("Tablo adi; harf/rakam/alt cizgi. Adlandirma icin get_dev_guide'daki tablo onek kuralina uy (ornegin Adm_T_...).")]
|
|
string tableName,
|
|
[Description("Is kolonlari. type: nvarchar, nvarchar(MAX), int, bigint, decimal (18,4), float, bit, datetime, datetime2, date, uniqueidentifier, money. maxLength yalnizca nvarchar icin (varsayilan 100). defaultValue: sayi, 'metin' ya da NEWID()/GETDATE()/GETUTCDATE()/SYSUTCDATETIME().")]
|
|
List<McpTableColumnDto> columns,
|
|
[Description("Tam audit kolonlari eklensin mi. Kullanici acikca istemedikce true birak.")]
|
|
bool fullAudited = true,
|
|
[Description("TenantId kolonu eklensin mi. Kullanici acikca istemedikce true birak.")]
|
|
bool multiTenant = true)
|
|
=> await developAppService.CreateTableAsync(new McpCreateTableRequestDto
|
|
{
|
|
TableName = tableName,
|
|
Columns = columns,
|
|
FullAudited = fullAudited,
|
|
MultiTenant = multiTenant
|
|
});
|
|
|
|
[McpServerTool(Name = McpToolNames.ListWizards, ReadOnly = true)]
|
|
[Description("Wizard File Manager'daki wizard dosyalarini listeler: dosya adi, ekran kodu, yol (List/Custom), menu yolu ve bagimli wizard'lar.")]
|
|
public async Task<List<WizardFileInfoDto>> ListWizardsAsync(
|
|
[Description("Dosya adi, wizard adi, ekran kodu ya da bilesen adi icinde aranacak metin. Bos birakilabilir.")]
|
|
string? search = null)
|
|
=> await developAppService.GetWizardsAsync(search);
|
|
|
|
[McpServerTool(Name = McpToolNames.GetWizard, ReadOnly = true)]
|
|
[Description("Bir wizard seed dosyasini diskteki bicimiyle dondurur (Wizard / ListForm / Fields bolumleri). Var olan bir ekrani degistirmeden once oku.")]
|
|
public async Task<McpWizardFileDto> GetWizardAsync(
|
|
[Description("list_wizards cikisindaki fileName degeri, ornegin 'Tags.json'.")]
|
|
string fileName)
|
|
=> await developAppService.GetWizardAsync(fileName);
|
|
|
|
[McpServerTool(Name = McpToolNames.DeployWizard, Destructive = true, Idempotent = false)]
|
|
[Description("Wizard'i deploy eder: ListForm ekrani (ComponentKind 0) ya da bir custom component'i menuye baglayan kayit (ComponentKind 1), yetkileri, dil anahtarlari ve menusu seed dosyalarina yazilir. Deploy veritabanina yazmaz: ekran, menu ve yetkiler kullanici Wizard Manager'da DB Migrate calistirinca olusur; cikistaki isPending true iken ekran henuz yoktur; migrate'i bekleme, isini bitir ve kullaniciya yalnizca DB Migrate calistirmasini soyle. Eksik modul, ust menu, izin grubu, yetki ve dil satirlari da deploy'da dosyalara yazilir ve migrate onlari dogru sirayla ekler; bunlarin veritabaninda olmamasi beklenen durumdur, uyari olarak raporlama ve kullanicidan elle eklemesini isteme. Ust menu zinciri istenen derinlikte kurulur: MenuParentCode ekranin dogrudan ust menusu, Menu.MenuParentPath onun ustundeki seviyeler (kokten asagi, her biri {Code, Icon, En, Tr}); eksik her seviye bir ustunun altina kurulur, var olana baglanilir. Liste ekraninda Grid, Card, Pivot ve Chart gorunumleri varsayilan aciktir (DefaultLayout grid); kullanici bir gorunumu istemedigini soylemedikce bu bayraklari false yazma. Hedef tablo onceden var olmalidir; yoksa once create_table ile olustur. Istek ornek/demo veri tasiyorsa (prompt'taki tablo/liste ya da ekran goruntusunde gorunen satirlar) SeedData true yaz ve satirlarin hepsini ayni cagrida seedRows ile gonder: satirlar ekranin data/{ListFormCode}.json dosyasina yazilir, migrate ekrani kurduktan sonra ekler. Satirlar icin migrate'i bekleyip insert_row kullanma. replaceFileName verilirse o wizard'in dosyalari yeniden yazilir, onceki kayitlarin yerini migrate'te yenileri alir; ekran wizard cevaplarindan yeniden turetildigi icin tasarimcida yapilmis ve wizard'da karsiligi olmayan ayarlar kaybolur. Ne yazacagini get_dev_guide 4. bolumunden ogren.")]
|
|
public async Task<WizardFileInfoDto?> DeployWizardAsync(
|
|
[Description("Wizard JSON'u: seed dosyasi bicimi ({\"Wizard\": {...}}, get_wizard cikisi gibi) ya da duz wizard cevaplari. Enum alanlari sayi olarak yazilir.")]
|
|
string wizard,
|
|
[Description("Guncellemede yerine gecilecek dosyanin adi (list_wizards cikisindaki fileName). Yeni wizard icin bos birak.")]
|
|
string? replaceFileName = null,
|
|
[Description("Demo satirlar: JSON nesne dizisi, anahtarlar ekranin alan adlari ([{\"Name\": \"...\"}]). Guid anahtar, TenantId ve audit alanlarini yazma; sunucu ve migrate doldurur. Yalnizca SeedData true olan liste ekraninda.")]
|
|
string? seedRows = null)
|
|
=> await developAppService.DeployWizardAsync(new McpWizardDeployRequestDto { Wizard = wizard, ReplaceFileName = replaceFileName, SeedRows = seedRows });
|
|
|
|
[McpServerTool(Name = McpToolNames.DeleteWizard, Destructive = true, Idempotent = true)]
|
|
[Description("Bir wizard'i ve yalnizca onun urettigi kayitlari siler; baska yerde kullanilan kayitlar korunur ve raporda gosterilir. Ekranin veri aynasi (data/{ListFormCode}.json) ekranla birlikte gider. Bagimli wizard'i olan bir wizard silinemez, once bagimlilar silinir. Ekranin tablosu varsayilan olarak kalir; dropTables true ise tablo DROP edilir, CRUD endpoint'leri ve sql/object + crud seed dosyalari da silinir. Tablo yalnizca create_table ya da tablo tasarimcisiyla olusturulmussa (sql/object scripti varsa) ve baska ekran, Custom Endpoint ya da bilesen kullanmiyorsa dusurulur; aksi halde raporda neden korundugu yazar. Tablo silmek geri alinamaz ve App.SqlQueryManager yetkisi ister: dropTables true gondermeden once kullaniciya tablonun ve icindeki verinin silinecegini soyle ve acik onay al.")]
|
|
public async Task<WizardDeleteResultDto> DeleteWizardAsync(
|
|
[Description("Silinecek wizard dosyasinin adi (list_wizards cikisindaki fileName).")]
|
|
string fileName,
|
|
[Description("Ekranin tablosu da dusurulsun mu. Yalnizca kullanici acikca onayladiysa true.")]
|
|
bool dropTables = false)
|
|
=> await developAppService.DeleteWizardAsync(fileName, dropTables);
|
|
|
|
[McpServerTool(Name = McpToolNames.VerifyWizard, ReadOnly = true)]
|
|
[Description("Deploy edilip DB Migrate ile kurulmus bir wizard'i veri yazmadan dogrular (migrate bekleyen wizard'da install kontrolu basarisiz doner ve diger kontroller yapilmaz): menu kaydi (var mi, etkin mi, URL'si wizard'la ayni mi), menunun istedigi yetki tanimi, liste ekraninda ekranin sorgusu (cagiranin yetkisiyle tek satir), Custom yolunda bilesen (var mi, etkin mi, rotasi menuyle ayni mi) ve seed kapanisi (migrate ya da baska ortamda eksik kalacak bagimlilik). passed yalnizca blocking kontrollerden biri kalirsa false olur; seed_closure engellemez ama raporlanmalidir. Istege baglidir: kullanici migrate sonrasi dogrulama isterse cagir; gelistirme talebi dongusunun adimi degildir.")]
|
|
public async Task<McpVerifyResultDto> VerifyWizardAsync(
|
|
[Description("Dogrulanacak wizard dosyasinin adi (list_wizards ya da deploy_wizard cikisindaki fileName).")]
|
|
string fileName)
|
|
=> await developAppService.VerifyWizardAsync(fileName);
|
|
|
|
[McpServerTool(Name = McpToolNames.StartDevRequest, Destructive = false, Idempotent = false)]
|
|
[Description("App.DevRequests kuyrugundaki bir gelistirme talebini ustlenir: talep Ready to Start durumundaysa In Progress'e alir ve talebin metnini (baslik, HTML aciklama, onaylanmis HTML analiz, oncelik, etiketler, SubTasks) dondurur; baska bir durumdaysa reddeder. Aciklamaya yapistirilmis gorseller ayri resim icerigi olarak gelir, aciklamada yerlerinde [image-N] isareti durur; gorseldeki ekran duzeni ve satirlar talebin parcasidir (demo veri icin deploy_wizard SeedData). Uretim aciklama ile analize birlikte dayanir; analizdeki menu, tablo ve kapsam kararlari onaylanmistir. Bir talep uzerinde calismaya bu aracla baslanir, Status'u update_row ile In Progress yapma. Sonraki adimlar icin get_dev_guide lowcode 0.11.")]
|
|
public async Task<IList<ContentBlock>> StartDevRequestAsync(
|
|
[Description("Talebin Id'si (GUID) ya da tam basligi. Ayni basligi tasiyan birden fazla talep varsa Id ver.")]
|
|
string request)
|
|
=> ToContent(await developAppService.StartDevRequestAsync(request));
|
|
|
|
[McpServerTool(Name = McpToolNames.GetDevRequest, ReadOnly = true)]
|
|
[Description("Bir gelistirme talebini durumuna dokunmadan okur; start_dev_request ile ayni cevabi (aciklama + ayri resim icerikleri) dondurur. Analiz oncesi talebi okumak, yarim kalmis (In Progress/Testing) bir talebe donmek ya da gorselleri yeniden gormek icin kullan; talebi ustlenmek icin degil.")]
|
|
public async Task<IList<ContentBlock>> GetDevRequestAsync(
|
|
[Description("Talebin Id'si (GUID) ya da tam basligi.")]
|
|
string request)
|
|
=> ToContent(await developAppService.GetDevRequestAsync(request));
|
|
|
|
[McpServerTool(Name = McpToolNames.SaveDevRequestAnalysis, Destructive = true, Idempotent = true)]
|
|
[Description("Backlog ya da Analysis durumundaki bir talebin analizini (HTML) Analysis alanina yazar ve talebi Analysis durumuna alir; var olan analiz degistirilir, Description'a dokunulmaz. Diger durumlarda reddeder. Once get_dev_request ile talebi ve gorsellerini oku; analizin bolumleri ve standardi get_dev_guide lowcode 0.12'dedir. Analizi begenen kullanici talebi Ready to Start'a kendisi alir; MCP almaz.")]
|
|
public async Task<IList<ContentBlock>> SaveDevRequestAnalysisAsync(
|
|
[Description("Talebin Id'si (GUID) ya da tam basligi.")]
|
|
string request,
|
|
[Description("Analizin tamami, HTML.")]
|
|
string html)
|
|
=> ToContent(await developAppService.SaveDevRequestAnalysisAsync(request, html));
|
|
|
|
[McpServerTool(Name = McpToolNames.AppendDevRequestNote, Destructive = false, Idempotent = false)]
|
|
[Description("Talebin Analysis alaninin sonuna HTML ekler; var olan analize ve Description'a dokunmaz. Description talebin yazildigi haliyle kalir; uretim sonuc notunu (<h4>MCP</h4> + uretilenler, varsayimlar ve DB Migrate bekleyen wizard'lar) bu aracla ekle. Description'i ya da Analysis'i update_row ile yazma.")]
|
|
public async Task<string> AppendDevRequestNoteAsync(
|
|
[Description("Talebin Id'si (GUID) ya da tam basligi.")]
|
|
string request,
|
|
[Description("Eklenecek HTML parcasi.")]
|
|
string html)
|
|
{
|
|
await developAppService.AppendDevRequestNoteAsync(request, html);
|
|
return "ok";
|
|
}
|
|
|
|
[McpServerTool(Name = McpToolNames.AddDevRequestSubTasks, Destructive = false, Idempotent = false)]
|
|
[Description("Talebin Sub Tasks listesine uretilen her artefakt icin tamamlanmis bir satir ekler; atanan kisi baglanan kullanici, bitis tarihi bugun olarak sunucuda yazilir, var olan satirlar korunur. SubTasks alanini update_row ile yazma.")]
|
|
public async Task<string> AddDevRequestSubTasksAsync(
|
|
[Description("Talebin Id'si (GUID) ya da tam basligi.")]
|
|
string request,
|
|
[Description("Eklenecek satir basliklari, ornegin [\"create_table: Hr_T_Certificate\", \"deploy_wizard: Certificates.json\"].")]
|
|
List<string> titles)
|
|
{
|
|
await developAppService.AddDevRequestSubTasksAsync(request, titles);
|
|
return "ok";
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Talep metni JSON olarak, gorseller ayri resim icerigi olarak doner; model resmi ancak bu
|
|
/// bicimde gorur, base64 metni olarak goremez.
|
|
/// </summary>
|
|
private static List<ContentBlock> ToContent(McpDevRequestDto devRequest)
|
|
{
|
|
List<ContentBlock> content = [new TextContentBlock { Text = JsonSerializer.Serialize(devRequest, TextOptions) }];
|
|
|
|
foreach (var image in devRequest.Images)
|
|
{
|
|
// Veri zaten base64; cozup yeniden kodlamak buyuk gorsellerde bosuna bellek harcar.
|
|
content.Add(new ImageContentBlock { Data = Encoding.UTF8.GetBytes(image.Data), MimeType = image.MimeType });
|
|
}
|
|
|
|
return content;
|
|
}
|
|
|
|
[McpServerTool(Name = McpToolNames.ListComponents, ReadOnly = true)]
|
|
[Description("Custom component'leri ad, rota ve aciklamasiyla listeler; kod dondurmez. isVisualDesigner true olanlarin kodu tasarimci belgesinden uretilir ve MCP ile degistirilemez.")]
|
|
public async Task<List<McpComponentSummaryDto>> ListComponentsAsync(
|
|
[Description("Ad, rota ya da aciklama icinde aranacak metin. Bos birakilabilir.")]
|
|
string? search = null)
|
|
=> await developAppService.GetComponentsAsync(search);
|
|
|
|
[McpServerTool(Name = McpToolNames.GetComponent, ReadOnly = true)]
|
|
[Description("Bir custom component'in kodunu, props'unu ve bagimliliklarini dondurur. Degistirmeden once oku.")]
|
|
public async Task<CustomComponentDto?> GetComponentAsync(
|
|
[Description("Bilesenin adi.")]
|
|
string name)
|
|
=> await developAppService.GetComponentAsync(name);
|
|
|
|
[McpServerTool(Name = McpToolNames.SaveComponent, Destructive = true, Idempotent = true)]
|
|
[Description("Custom component'i ada gore kaydeder: yoksa ekler, varsa gunceller. Guncellemede verilmeyen alanlar korunur. Kod kurallari ve kullanilabilir kutuphaneler icin get_dev_guide 7. bolumu. Bilesen menude gorunecekse ardindan ComponentKind 1 ile deploy_wizard cagir.")]
|
|
public async Task<CustomComponentDto> SaveComponentAsync(
|
|
[Description("Bilesenin adi; kimligidir.")]
|
|
string name,
|
|
[Description("Bilesenin React kodu. Yeni bilesende zorunlu, guncellemede bos birakilirsa mevcut kod korunur.")]
|
|
string? code = null,
|
|
[Description("'/' ile baslayan rota. '/admin/' ile baslamayan rota public sayfa olur. Bos dize rotayi kaldirir, verilmezse mevcut rota korunur.")]
|
|
string? routePath = null,
|
|
[Description("Aciklama.")]
|
|
string? description = null,
|
|
[Description("Props JSON metni. Tasarimci belgesi (visualDesigner) iceremez.")]
|
|
string? props = null,
|
|
[Description("Bagimli bilesen adlarinin JSON dizisi, ornegin [\"PublicHeader\"].")]
|
|
string? dependencies = null,
|
|
[Description("Bilesen etkin mi. Yeni bilesende varsayilan true.")]
|
|
bool? isActive = null)
|
|
=> await developAppService.SaveComponentAsync(new McpComponentSaveRequestDto
|
|
{
|
|
Name = name,
|
|
Code = code,
|
|
RoutePath = routePath,
|
|
Description = description,
|
|
Props = props,
|
|
Dependencies = dependencies,
|
|
IsActive = isActive
|
|
});
|
|
|
|
[McpServerTool(Name = McpToolNames.DeleteComponent, Destructive = true, Idempotent = false)]
|
|
[Description("Custom component'i siler. Bilesene bagli bir Custom wizard varsa once delete_wizard ile o silinir.")]
|
|
public async Task DeleteComponentAsync(
|
|
[Description("Bilesenin adi.")]
|
|
string name)
|
|
=> await developAppService.DeleteComponentAsync(name);
|
|
}
|