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

100 lines
4.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol;
using ModelContextProtocol.AspNetCore;
using ModelContextProtocol.Protocol;
using Sozsoft.Mcp.Tools;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Modularity;
namespace Sozsoft.Mcp;
/// <summary>
/// /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 <c>MapMcp</c> ile baglanir.
/// </summary>
[DependsOn(
typeof(McpApplicationContractsModule),
typeof(AbpAspNetCoreMvcModule)
)]
public class McpHttpApiModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<McpOAuthOptions>(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<McpSchemaTools>()
.WithTools<McpQueryTools>()
.WithTools<McpDataTools>()
.WithTools<McpDevelopTools>()
.WithTools<McpReportTools>()
.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<IMcpSchemaAppService>();
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 new McpException(await requestContext.Services!.GetRequiredService<McpToolErrorTranslator>().TranslateAsync(ex, null), ex);
}
return result;
});
// MCP'nin kendi reddi istisna degildir (McpProblem). Arkadaki platform servisinin yetki
// reddi ve is kurali hatasi ise istisnadir; firlatilirsa SDK onu yigin izli ERR olarak
// loglar. Hata sonucu (IsError) olarak donulur; model nedeni okuyup devam eder.
filters.AddCallToolFilter(next => async (requestContext, cancellationToken) =>
{
try
{
return await next(requestContext, cancellationToken);
}
catch (System.Exception ex) when (McpToolErrorTranslator.CanTranslate(ex))
{
var message = await requestContext.Services!.GetRequiredService<McpToolErrorTranslator>().TranslateAsync(ex, requestContext.Params?.Name);
return McpToolResult.Error(message);
}
});
});
}
}