mcp hatası düzeltildi.
This commit is contained in:
parent
5e8ee03fc8
commit
cd41d9dff3
7 changed files with 96 additions and 4 deletions
|
|
@ -248,7 +248,12 @@ public class McpDevelopAppService(
|
||||||
return Problem(McpErrorCodes.InvalidIdentifier, entityName ?? string.Empty);
|
return Problem(McpErrorCodes.InvalidIdentifier, entityName ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
var seedFile = CrudEndpointSeedFileDto.Generate(entity, operationTypes, objectType, Clock.Now.ToUniversalTime());
|
// Model listeyi JSON dizisi olarak da gonderebilir (["GetList","Create"]); metne cevrilen dizi
|
||||||
|
// virgullu listeye indirilir.
|
||||||
|
var operations = operationTypes is null
|
||||||
|
? null
|
||||||
|
: string.Join(',', operationTypes.Split(',').Select(item => item.Trim('[', ']', '"', ' ')));
|
||||||
|
var seedFile = CrudEndpointSeedFileDto.Generate(entity, operations, objectType, Clock.Now.ToUniversalTime());
|
||||||
seedFile.Write(configuration);
|
seedFile.Write(configuration);
|
||||||
await timesheetRecorder.RecordAsync(McpTimesheetTexts.GenerateCrudEndpoints, entity);
|
await timesheetRecorder.RecordAsync(McpTimesheetTexts.GenerateCrudEndpoints, entity);
|
||||||
|
|
||||||
|
|
@ -319,6 +324,16 @@ public class McpDevelopAppService(
|
||||||
var previous = wizard.EditFileName is null ? null : (await wizardAppService.GetFile(wizard.EditFileName)).Wizard;
|
var previous = wizard.EditFileName is null ? null : (await wizardAppService.GetFile(wizard.EditFileName)).Wizard;
|
||||||
wizard.MenuOrder = previous?.MenuOrder ?? 0;
|
wizard.MenuOrder = previous?.MenuOrder ?? 0;
|
||||||
|
|
||||||
|
// deploy_wizard dosyayi tamamen degistirir. Duzenlemede WizardCode yazilmamissa kod sessizce
|
||||||
|
// App.Wizard.{Ad}'a duser: girdi kismidir ve deploy ekranin menusunu, yetkilerini ve alanlarini
|
||||||
|
// bosaltirdi. Kodu bilerek degistiren girdi WizardCode'u acikca yazar.
|
||||||
|
if (previous is not null && string.IsNullOrWhiteSpace(wizard.ListFormCode) &&
|
||||||
|
!string.IsNullOrWhiteSpace(previous.ListFormCode) &&
|
||||||
|
!string.Equals(previous.ListFormCode.Trim(), wizard.ResolveListFormCode(), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return Problem(McpErrorCodes.WizardCodeMissing, wizard.EditFileName!, previous.ListFormCode.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
// Alt form sekmesi ana kayit her degistiginde yeniden sorgulanmaz; MCP yeni baglanti
|
// Alt form sekmesi ana kayit her degistiginde yeniden sorgulanmaz; MCP yeni baglanti
|
||||||
// acarken yenilemeyi kapali yazar. Var olan baglantida kullanicinin ekrandan verdigi
|
// acarken yenilemeyi kapali yazar. Var olan baglantida kullanicinin ekrandan verdigi
|
||||||
// deger korunur.
|
// deger korunur.
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ public static class McpErrorCodes
|
||||||
public const string DevGuideSearchMatches = Prefix + "DevGuideSearchMatches";
|
public const string DevGuideSearchMatches = Prefix + "DevGuideSearchMatches";
|
||||||
public const string InvalidWizard = Prefix + "InvalidWizard";
|
public const string InvalidWizard = Prefix + "InvalidWizard";
|
||||||
public const string InvalidWizardValue = Prefix + "InvalidWizardValue";
|
public const string InvalidWizardValue = Prefix + "InvalidWizardValue";
|
||||||
|
public const string WizardCodeMissing = Prefix + "WizardCodeMissing";
|
||||||
public const string SeedRowsInvalid = Prefix + "SeedRowsInvalid";
|
public const string SeedRowsInvalid = Prefix + "SeedRowsInvalid";
|
||||||
public const string SeedRowsNotAllowed = Prefix + "SeedRowsNotAllowed";
|
public const string SeedRowsNotAllowed = Prefix + "SeedRowsNotAllowed";
|
||||||
public const string SeedRowsUnknownFields = Prefix + "SeedRowsUnknownFields";
|
public const string SeedRowsUnknownFields = Prefix + "SeedRowsUnknownFields";
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||||
using ModelContextProtocol;
|
using ModelContextProtocol;
|
||||||
using ModelContextProtocol.AspNetCore;
|
using ModelContextProtocol.AspNetCore;
|
||||||
using ModelContextProtocol.Protocol;
|
using ModelContextProtocol.Protocol;
|
||||||
|
using ModelContextProtocol.Server;
|
||||||
using Sozsoft.Mcp.Tools;
|
using Sozsoft.Mcp.Tools;
|
||||||
using Volo.Abp.AspNetCore.Mvc;
|
using Volo.Abp.AspNetCore.Mvc;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
@ -85,6 +86,8 @@ public class McpHttpApiModule : AbpModule
|
||||||
// loglar. Hata sonucu (IsError) olarak donulur; model nedeni okuyup devam eder.
|
// loglar. Hata sonucu (IsError) olarak donulur; model nedeni okuyup devam eder.
|
||||||
filters.AddCallToolFilter(next => async (requestContext, cancellationToken) =>
|
filters.AddCallToolFilter(next => async (requestContext, cancellationToken) =>
|
||||||
{
|
{
|
||||||
|
McpToolArgumentNormalizer.Normalize(requestContext.Params, requestContext.MatchedPrimitive as McpServerTool);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await next(requestContext, cancellationToken);
|
return await next(requestContext, cancellationToken);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using ModelContextProtocol.Protocol;
|
||||||
|
using ModelContextProtocol.Server;
|
||||||
|
|
||||||
|
namespace Sozsoft.Mcp;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Model metin parametresine tirnaksiz deger (<c>section: 4.2</c>) ya da JSON metni yerine dogrudan
|
||||||
|
/// dizi/nesne (<c>filter: ["Status","=","Open"]</c>) gonderebilir. SDK her parametreyi ayri ayri
|
||||||
|
/// <c>string</c> olarak bagladigi icin bu deger <see cref="JsonException"/> ("Cannot get the value of a
|
||||||
|
/// token type 'Number' / 'StartArray' as a string") ile duser. Deger cagri araca ulasmadan, aracin kendi
|
||||||
|
/// semasina bakilarak ham JSON metnine cevrilir — JSON bekleyen parametre ayni metni alir; string icin
|
||||||
|
/// ozel converter kullanilmaz, cunku arac semasindaki <c>"type": "string"</c> bilgisini siler.
|
||||||
|
/// </summary>
|
||||||
|
internal static class McpToolArgumentNormalizer
|
||||||
|
{
|
||||||
|
public static void Normalize(CallToolRequestParams? parameters, McpServerTool? tool)
|
||||||
|
{
|
||||||
|
if (parameters?.Arguments is not { Count: > 0 } arguments
|
||||||
|
|| tool is null
|
||||||
|
|| !tool.ProtocolTool.InputSchema.TryGetProperty("properties", out var properties)
|
||||||
|
|| properties.ValueKind != JsonValueKind.Object)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, JsonElement>? normalized = null;
|
||||||
|
|
||||||
|
foreach (var (name, value) in arguments)
|
||||||
|
{
|
||||||
|
if (value.ValueKind is JsonValueKind.String or JsonValueKind.Null or JsonValueKind.Undefined
|
||||||
|
|| !properties.TryGetProperty(name, out var schema)
|
||||||
|
|| !IsStringOnly(schema))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
normalized ??= new Dictionary<string, JsonElement>(arguments);
|
||||||
|
normalized[name] = JsonSerializer.SerializeToElement(value.GetRawText());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalized is not null)
|
||||||
|
{
|
||||||
|
parameters.Arguments = normalized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nullable metin parametresi semada ["string", "null"] olarak gelir; baska tip de kabul eden bir
|
||||||
|
// birlesim tipi donusturulmez, degeri SDK oldugu gibi baglar.
|
||||||
|
private static bool IsStringOnly(JsonElement schema)
|
||||||
|
{
|
||||||
|
if (!schema.TryGetProperty("type", out var type))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return type.ValueKind switch
|
||||||
|
{
|
||||||
|
JsonValueKind.String => type.GetString() == "string",
|
||||||
|
JsonValueKind.Array => type.EnumerateArray()
|
||||||
|
.Select(item => item.GetString())
|
||||||
|
.Where(item => item != "null")
|
||||||
|
.SequenceEqual(["string"]),
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -101,7 +101,7 @@ public sealed class McpDevelopTools(IMcpDevelopAppService developAppService, IMc
|
||||||
public async Task<CallToolResult> DeployWizardAsync(
|
public async Task<CallToolResult> DeployWizardAsync(
|
||||||
[Description("Wizard JSON'u: seed dosyasi bicimi ({\"Wizard\": {...}, \"ListForm\": {...}, \"Fields\": [...]}, get_wizard cikisi gibi) ya da duz wizard cevaplari. ListForm/Fields istege baglidir ve kismi olabilir. Enum alanlari sayi olarak yazilir.")]
|
[Description("Wizard JSON'u: seed dosyasi bicimi ({\"Wizard\": {...}, \"ListForm\": {...}, \"Fields\": [...]}, get_wizard cikisi gibi) ya da duz wizard cevaplari. ListForm/Fields istege baglidir ve kismi olabilir. Enum alanlari sayi olarak yazilir.")]
|
||||||
string wizard,
|
string wizard,
|
||||||
[Description("Guncellemede yerine gecilecek dosyanin adi (list_wizards cikisindaki fileName). Yeni wizard icin bos birak.")]
|
[Description("Guncellemede yerine gecilecek dosyanin adi (list_wizards cikisindaki fileName). Yeni wizard icin bos birak. Guncelleme dosyayi tamamen degistirir: wizard girdisi get_wizard ile okunan dosyanin degistirilmis tam halidir; Wizard bolumu (WizardCode, Menu, Language, Groups) eksiksiz gonderilir.")]
|
||||||
string? replaceFileName = null,
|
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.")]
|
[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)
|
string? seedRows = null)
|
||||||
|
|
|
||||||
|
|
@ -1776,7 +1776,11 @@ public class ListFormWizardAppService(
|
||||||
$"'{wizardName}' adında bir Wizard zaten var ({duplicateFile}). Farklı bir ad verin ya da mevcut kaydı Wizard Manager üzerinden düzenleyin.");
|
$"'{wizardName}' adında bir Wizard zaten var ({duplicateFile}). Farklı bir ad verin ya da mevcut kaydı Wizard Manager üzerinden düzenleyin.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previous?.InsertedRecords?.MenuCodes.Contains(code, StringComparer.OrdinalIgnoreCase) == true)
|
// Migrate bekleyen onceki surumun menusu (Pending.Replaced) de bu wizard'indir: veritabaninda
|
||||||
|
// durur ama yeni deploy onu geri alabilir; aksi halde kodunu degistiren deploy'u geri almak
|
||||||
|
// kendi menusuyle catisir.
|
||||||
|
var owned = (previous?.InsertedRecords ?? new WizardInsertedRecordsDto()).Union(previous?.Pending?.Replaced);
|
||||||
|
if (owned.MenuCodes.Contains(code, StringComparer.OrdinalIgnoreCase))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (rows.Contains(WizardSeedRowStore.MenuFile, nameof(Menu.Code), code) ||
|
if (rows.Contains(WizardSeedRowStore.MenuFile, nameof(Menu.Code), code) ||
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ public static class PlatformConsts
|
||||||
public const string AdminNameDefaultValue = "Sedat";
|
public const string AdminNameDefaultValue = "Sedat";
|
||||||
public const string AdminSurNameDefaultValue = "ÖZTÜRK";
|
public const string AdminSurNameDefaultValue = "ÖZTÜRK";
|
||||||
public const string AdminEmailDefaultValue = "system@sozsoft.com";
|
public const string AdminEmailDefaultValue = "system@sozsoft.com";
|
||||||
public const string AdminPasswordDefaultValue = "1q2w3E*";
|
public const string AdminPasswordDefaultValue = "K1r1Ut7Z2o@@";
|
||||||
public const string AdminPhoneNumberDefaultValue = "05449476346";
|
public const string AdminPhoneNumberDefaultValue = "05449476346";
|
||||||
public const string AdminRocketUsernameDefaultValue = "sedat.ozturk";
|
public const string AdminRocketUsernameDefaultValue = "sedat.ozturk";
|
||||||
public const string AdminWorkHourDefaultValue = "00:00-23:59";
|
public const string AdminWorkHourDefaultValue = "00:00-23:59";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue