Files
EugeneTes e6b09fef1e feat(mcp): extension project and configuration
McpOptions/McpServerConfig/PlaywrightOptions with ApplyEnvironment (PLAYWRIGHT_* variables, quote-aware ExtraArgs split) and Validate; ShellWords; mcpServers from the user settings.json. New project in the solution and referenced by the extension tests.
2026-09-26 20:13:50 +02:00

50 lines
2.3 KiB
C#

using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
namespace Volcanic.AgenticCore.Extensions.Mcp;
/// <summary>Reads <c>"mcpServers"</c> from <c>&lt;agent dir&gt;/settings.json</c> (the same file that lists extensions).</summary>
internal static class McpUserSettings
{
public const string AgentDirVariable = "VOLCANIC_AGENT_DIR";
private static readonly JsonSerializerOptions Json = new()
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
Converters = { new JsonStringEnumConverter() },
};
/// <summary><c>VOLCANIC_AGENT_DIR</c>, else <c>~/.volcanic/agent</c>.</summary>
public static string AgentDir(Func<string, string?> getEnv) =>
getEnv(AgentDirVariable) is { Length: > 0 } dir
? dir
: Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".volcanic", "agent");
/// <summary>Servers in document order. A missing file or section is empty; a malformed file is logged and ignored.</summary>
public static IReadOnlyList<KeyValuePair<string, McpServerConfig>> Load(string agentDir, ILogger logger)
{
var path = Path.Combine(agentDir, "settings.json");
if (!File.Exists(path)) return [];
try
{
using var doc = JsonDocument.Parse(File.ReadAllText(path),
new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true });
if (doc.RootElement.ValueKind != JsonValueKind.Object
|| !doc.RootElement.TryGetProperty("mcpServers", out var servers)
|| servers.ValueKind != JsonValueKind.Object) return [];
var result = new List<KeyValuePair<string, McpServerConfig>>();
foreach (var server in servers.EnumerateObject())
result.Add(new(server.Name, server.Value.Deserialize<McpServerConfig>(Json) ?? new McpServerConfig()));
return result;
}
catch (Exception ex) when (ex is JsonException or IOException or UnauthorizedAccessException or NotSupportedException)
{
logger.LogWarning("Ignoring mcpServers in {Path}: {Message}", path, ex.Message);
return [];
}
}
}