diff --git a/src/Infrastructure/BotSharp.Abstraction/MCP/Services/IMcpClientHeaderProvider.cs b/src/Infrastructure/BotSharp.Abstraction/MCP/Services/IMcpClientHeaderProvider.cs new file mode 100644 index 000000000..fd591a660 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/MCP/Services/IMcpClientHeaderProvider.cs @@ -0,0 +1,30 @@ +namespace BotSharp.Abstraction.MCP.Services; + +/// +/// An optional host hook over the HTTP headers used to open one MCP server connection. +/// +/// +/// Nothing registers this by default. With no implementation registered the headers configured +/// under MCP:McpServerConfigs are used verbatim, which is the only behaviour there was +/// before the hook existed — a host that does not implement it sees no change at all. +/// +/// It exists so a host can call a server as whoever is driving the conversation instead of with +/// one fixed credential. That decision belongs to the host: it is the only side that knows what +/// a caller's credential is and which servers may be shown it. +/// +/// +public interface IMcpClientHeaderProvider +{ + /// + /// Answers the headers to send to . + /// + /// + /// The headers from configuration. This dictionary is shared for the lifetime of the process, + /// so an implementation that changes a header MUST copy it rather than write into it. + /// + /// + /// The headers to send. Returning unchanged is the no-op answer, + /// and is the answer expected for any server the implementation does not recognise. + /// + Dictionary? GetHeaders(string serverId, Dictionary? configured); +} diff --git a/src/Infrastructure/BotSharp.Core/MCP/Managers/McpClientManager.cs b/src/Infrastructure/BotSharp.Core/MCP/Managers/McpClientManager.cs index a07792919..8e21d74ef 100644 --- a/src/Infrastructure/BotSharp.Core/MCP/Managers/McpClientManager.cs +++ b/src/Infrastructure/BotSharp.Core/MCP/Managers/McpClientManager.cs @@ -34,7 +34,7 @@ public McpClientManager( { Name = config.Name, Endpoint = new Uri(config.HttpConfig.EndPoint), - AdditionalHeaders = config.HttpConfig.AdditionalHeaders, + AdditionalHeaders = ResolveHeaders(config.Id, config.HttpConfig.AdditionalHeaders), ConnectionTimeout = config.HttpConfig.ConnectionTimeout }); } @@ -44,7 +44,7 @@ public McpClientManager( { Name = config.Name, Endpoint = new Uri(config.SseConfig.EndPoint), - AdditionalHeaders = config.SseConfig.AdditionalHeaders, + AdditionalHeaders = ResolveHeaders(config.Id, config.SseConfig.AdditionalHeaders), ConnectionTimeout = config.SseConfig.ConnectionTimeout }); } @@ -74,6 +74,21 @@ public McpClientManager( } } + /// + /// The headers to open a connection with: the ones from configuration, unless the host has + /// registered an that wants to adjust them. + /// + /// + /// No provider is registered by default, and a provider is free to answer with what it was + /// given, so a host without one — or with one that does not recognise this server — gets the + /// configured headers back untouched. + /// + private Dictionary? ResolveHeaders(string serverId, Dictionary? configured) + { + var provider = _services.GetService(); + return provider == null ? configured : provider.GetHeaders(serverId, configured); + } + public void Dispose() {