Files
Playa/extensions/Volcanic.AgenticCore.Extensions.Mcp/Remote/RemoteMcpOptions.cs
EugeneTes 891a644590 feat(mcp): remote MCP servers per chat with sealed keys and owner-only commands
Streamable HTTP/SSE servers from the workspace's .volcanic/mcp/servers.json.
Headers are sealed with AES-GCM; the chat and the visible fields are the
associated data, so only /mcp_add|remove|tools change servers. Tools
<server>__<tool> go live in the session. An unreachable server is skipped
and reported in the system prompt. Keys are redacted from tool results, and
Telegram deletes messages that carry one.
2026-10-09 21:11:01 +00:00

38 lines
1.8 KiB
C#

namespace Volcanic.AgenticCore.Extensions.Mcp.Remote;
/// <summary>
/// Remote MCP servers per chat (<see cref="RemoteMcpExtension"/>). Process-wide; the servers themselves are per chat.
/// Without <see cref="Secret"/> the commands only explain how to turn the feature on.
/// </summary>
public sealed record RemoteMcpOptions
{
/// <summary>
/// Derives the key that seals each chat's server headers. Must be a host secret the agent's processes never see (the
/// Telegram host uses <c>MCP_ENCRYPTION_SECRET</c>, else the bot token). Changing it makes every stored server unreadable.
/// </summary>
public string? Secret { get; init; }
/// <summary>Handshake plus <c>tools/list</c>, per server. A server that misses it starts the session without its tools.</summary>
public TimeSpan ConnectTimeout { get; init; } = TimeSpan.FromSeconds(15);
/// <summary>One <c>tools/call</c>.</summary>
public TimeSpan CallTimeout { get; init; } = TimeSpan.FromSeconds(120);
/// <summary>First retry delay for a server that failed to connect; doubles per failure up to <see cref="MaxRetryDelay"/>.</summary>
public TimeSpan RetryDelay { get; init; } = TimeSpan.FromSeconds(60);
public TimeSpan MaxRetryDelay { get; init; } = TimeSpan.FromMinutes(15);
/// <summary>How long <c>/mcp_add</c> waits for the key when it asks for one.</summary>
public TimeSpan KeyPromptTimeout { get; init; } = TimeSpan.FromMinutes(5);
/// <summary>Tests pass a handler over an in-memory server; null uses one shared socket handler.</summary>
public Func<HttpMessageHandler>? HttpHandlerFactory { get; init; }
public TimeProvider? Time { get; init; }
public bool IsConfigured => !string.IsNullOrWhiteSpace(Secret);
public override string ToString() => $"RemoteMcpOptions {{ Configured = {IsConfigured}, ConnectTimeout = {ConnectTimeout} }}";
}