sozsoft-platform/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpAppService.cs
2026-10-11 01:03:36 +03:00

98 lines
3.6 KiB
C#

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Sozsoft.Mcp.Dtos;
using Sozsoft.Mcp.Services;
using Sozsoft.Platform.Localization;
using Volo.Abp.Application.Services;
namespace Sozsoft.Mcp;
/// <summary>
/// MCP uygulama servislerinin ortak tabani. Kullaniciya gorunen metinler platformun
/// <c>PlatformResource</c> kaynagindan okunur; sunucu kaydini cozme ve arac kapisi her
/// serviste ayni davranir.
/// </summary>
public abstract class McpAppService : ApplicationService
{
protected IMcpServerConfigProvider ServerConfigProvider { get; }
protected McpAppService(IMcpServerConfigProvider serverConfigProvider)
{
ServerConfigProvider = serverConfigProvider;
LocalizationResource = typeof(PlatformResource);
}
/// <summary>Etkin sunucu kaydinin DTO hali; kayit yoksa null.</summary>
protected async Task<McpServerConfigDto?> ResolveServerConfigAsync()
{
var server = await ServerConfigProvider.GetEffectiveServerAsync();
if (server is null)
{
return null;
}
return new McpServerConfigDto
{
Name = server.Name,
Url = server.Url,
AllowedTools = [.. ServerConfigProvider.SplitMultiValue(server.AllowedTools)],
MaxRows = server.MaxRows,
Description = server.Description,
TenantName = CurrentTenant.Name
};
}
/// <summary>
/// Kapali bir araca yapilan cagriyi reddeder: sunucu kaydi yoksa, arac sunucu kaydinda kapaliysa
/// ya da kullanicinin uygulamada karsilik gelen yetkisi yoksa. Arac listesini suzmek tek basina
/// yeterli degildir — istemci listede olmayan bir araci da cagirabilir.
/// </summary>
protected async Task<McpOutcome<McpServerConfigDto>> EnsureToolAllowedAsync(string toolName)
{
var config = await ResolveServerConfigAsync();
if (config is null)
{
return Problem(McpErrorCodes.NoServerRecord);
}
if (CurrentTenant.IsAvailable && !McpToolNames.TenantTools.Contains(toolName, StringComparer.Ordinal))
{
return Problem(McpErrorCodes.TenantToolDisabled, toolName);
}
if (!McpToolPolicy.IsAllowed(true, CurrentTenant.IsAvailable, config.AllowedTools, toolName))
{
return Problem(McpErrorCodes.ToolDisabled, toolName, config.Name);
}
if (McpToolNames.RequiredPermissions.TryGetValue(toolName, out var permission) &&
await CheckPermissionAsync(toolName, permission) is { } denied)
{
return denied;
}
return config;
}
/// <summary>
/// Yetkiyi adiyla ister; eksikse istemcinin rol ekraninda arayabilecegi ad ve kodla reddeder.
/// <c>AuthorizationService.CheckAsync</c> istisna atar, bu yuzden MCP'de kullanilmaz.
/// </summary>
protected async Task<McpProblem?> CheckPermissionAsync(string toolName, string permission)
=> await AuthorizationService.IsGrantedAsync(permission)
? null
: Problem(McpErrorCodes.PermissionDenied, toolName, McpPermissionText.Describe(L, permission));
protected McpProblem Problem(string name, params object[] arguments)
=> McpProblem.From(L, name, arguments);
/// <summary>Istenen satir sayisini sunucu kaydindaki tavana kirpar.</summary>
protected static int ResolveMaxRows(McpServerConfigDto config, int requested)
{
var ceiling = config.MaxRows > 0 ? config.MaxRows : McpConsts.DefaultMaxRows;
return requested > 0 ? Math.Min(requested, ceiling) : ceiling;
}
}