2026-09-28 09:28:56 +00:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Linq;
|
2026-09-17 09:01:14 +00:00
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
2026-10-09 17:02:27 +00:00
|
|
|
|
using ModelContextProtocol;
|
2026-09-17 09:01:14 +00:00
|
|
|
|
using ModelContextProtocol.AspNetCore;
|
2026-10-09 17:02:27 +00:00
|
|
|
|
using ModelContextProtocol.Protocol;
|
2026-10-17 22:31:09 +00:00
|
|
|
|
using ModelContextProtocol.Server;
|
2026-09-17 09:01:14 +00:00
|
|
|
|
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('/');
|
2026-09-28 09:28:56 +00:00
|
|
|
|
// 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;
|
2026-09-17 09:01:14 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
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>()
|
2026-09-18 13:47:23 +00:00
|
|
|
|
.WithTools<McpDevelopTools>()
|
2026-10-01 12:01:57 +00:00
|
|
|
|
.WithTools<McpReportTools>()
|
2026-09-24 11:58:23 +00:00
|
|
|
|
.WithPrompts(typeof(McpDevelopPrompts))
|
|
|
|
|
|
.WithRequestFilters(filters =>
|
2026-09-17 09:01:14 +00:00
|
|
|
|
{
|
2026-09-24 11:58:23 +00:00
|
|
|
|
filters.AddListToolsFilter(next => async (requestContext, cancellationToken) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = await next(requestContext, cancellationToken);
|
2026-09-17 09:01:14 +00:00
|
|
|
|
|
2026-09-24 11:58:23 +00:00
|
|
|
|
// 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))
|
|
|
|
|
|
{
|
2026-10-09 17:02:27 +00:00
|
|
|
|
throw new McpException(await requestContext.Services!.GetRequiredService<McpToolErrorTranslator>().TranslateAsync(ex, null), ex);
|
2026-09-24 11:58:23 +00:00
|
|
|
|
}
|
2026-09-17 09:01:14 +00:00
|
|
|
|
|
2026-09-24 11:58:23 +00:00
|
|
|
|
return result;
|
|
|
|
|
|
});
|
2026-09-17 09:01:14 +00:00
|
|
|
|
|
2026-10-10 22:03:36 +00:00
|
|
|
|
// 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.
|
2026-09-24 11:58:23 +00:00
|
|
|
|
filters.AddCallToolFilter(next => async (requestContext, cancellationToken) =>
|
|
|
|
|
|
{
|
2026-10-17 22:31:09 +00:00
|
|
|
|
McpToolArgumentNormalizer.Normalize(requestContext.Params, requestContext.MatchedPrimitive as McpServerTool);
|
|
|
|
|
|
|
2026-09-24 11:58:23 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
return await next(requestContext, cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (System.Exception ex) when (McpToolErrorTranslator.CanTranslate(ex))
|
|
|
|
|
|
{
|
2026-10-09 17:02:27 +00:00
|
|
|
|
var message = await requestContext.Services!.GetRequiredService<McpToolErrorTranslator>().TranslateAsync(ex, requestContext.Params?.Name);
|
2026-10-10 22:03:36 +00:00
|
|
|
|
return McpToolResult.Error(message);
|
2026-09-24 11:58:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2026-09-17 09:01:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|