mcp hatası düzeltildi.

This commit is contained in:
Sedat Öztürk 2026-10-18 01:31:09 +03:00
parent 5e8ee03fc8
commit cd41d9dff3
7 changed files with 96 additions and 4 deletions

View file

@ -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.

View file

@ -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";

View file

@ -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);

View file

@ -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
};
}
}

View file

@ -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)

View file

@ -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) ||

View file

@ -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";