using System; using System.Linq; using Microsoft.Extensions.DependencyInjection; using ModelContextProtocol.AspNetCore; using Sozsoft.Mcp.Tools; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Modularity; namespace Sozsoft.Mcp; /// /// /mcp ucunun sunum katmani. Araclar yalnizca uygulama sozlesmesini tanir; kimlik dogrulama, /// kiraci cozumu, unit of work ve yetki kontrolu istegin gectigi ABP ardisik duzeninden gelir. /// Ucun kendisi host tarafinda MapMcp ile baglanir. /// [DependsOn( typeof(McpApplicationContractsModule), typeof(AbpAspNetCoreMvcModule) )] public class McpHttpApiModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); Configure(options => { options.BaseUrl = (configuration["App:SelfUrl"] ?? string.Empty).TrimEnd('/'); // OpenIddict keşif belgesindeki issuer'ı Uri.AbsoluteUri ile (sonda eğik çizgiyle) yazar; // RFC 8414 §3.3 gereği istemci iki değeri birebir karşılaştırır. var authority = configuration["AuthServer:Authority"]; options.AuthorizationServer = string.IsNullOrWhiteSpace(authority) ? string.Empty : new Uri(authority).AbsoluteUri; }); context.Services .AddMcpServer(options => { options.ServerInfo = new() { Name = McpServerInfoDefaults.Name, Version = McpServerInfoDefaults.Version }; options.ServerInstructions = McpServerInfoDefaults.Instructions; }) .WithHttpTransport(options => { // Durumsuz kip: her istek kendi ABP kapsaminda kosar, boylece kimlik, kiraci // cozumu ve unit of work istekle birlikte dogru kurulur. Durumlu kipte oturum // kapsami initialize istegine baglanir ve sonraki cagrilar yanlis kiracinin // baglaminda kalabilir; ayrica ters vekil arkasinda oturum yapisakligi gerekmez. options.SessionMode = HttpServerSessionMode.Stateless; }) .WithTools() .WithTools() .WithTools() .WithTools() .WithTools() .WithPrompts(typeof(McpDevelopPrompts)) .WithRequestFilters(filters => { filters.AddListToolsFilter(next => async (requestContext, cancellationToken) => { var result = await next(requestContext, cancellationToken); // Arac listesi kiracinin sunucu kaydina ve kullanicinin uygulamadaki yetkilerine // gore suzulur. Cagri aninda da ayni kural uygulanir // (McpAppService.EnsureToolAllowedAsync): istemci listede olmayan bir araci da // cagirabilir, suzme tek basina bir yetki siniri degildir. var schemaAppService = requestContext.Services!.GetRequiredService(); try { var available = (await schemaAppService.GetAvailableToolsAsync()).ToHashSet(System.StringComparer.Ordinal); result.Tools = [.. result.Tools.Where(tool => available.Contains(tool.Name))]; } catch (System.Exception ex) when (McpToolErrorTranslator.CanTranslate(ex)) { throw await requestContext.Services!.GetRequiredService().TranslateAsync(ex, null); } return result; }); // SDK arac hatasinin mesajini yalnizca McpException ise iletir; yetki reddi ve is // kurali hatalari burada cevrilir ki istemci nedenini (eksik yetkiyi) gorsun. filters.AddCallToolFilter(next => async (requestContext, cancellationToken) => { try { return await next(requestContext, cancellationToken); } catch (System.Exception ex) when (McpToolErrorTranslator.CanTranslate(ex)) { throw await requestContext.Services!.GetRequiredService().TranslateAsync(ex, requestContext.Params?.Name); } }); }); } }