1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
using System.Globalization;
using System.Text.Json;
using Agent.Core.Localization;
using Agent.Mcp;
using System.Collections.Concurrent;
namespace Agent.App.Logging;
public sealed class McpHostLogObserver : IMcpHostObserver, IDisposable
{
private readonly RollingFileLog _application;
private readonly RollingFileWriter? _http;
private readonly LocaleCatalog _locale;
private readonly string _logDirectory;
private readonly ConcurrentDictionary<string, byte> _observedConnections = new(StringComparer.Ordinal);
public McpHostLogObserver(RollingFileLog application, LocaleCatalog locale, string logDirectory,
bool httpRequests, long maxBytes, int retainedFiles)
{
_application = application;
_locale = locale;
_logDirectory = logDirectory;
if (httpRequests) _http = new RollingFileWriter(Path.Combine(logDirectory, "http.jsonl"), maxBytes, retainedFiles);
}
public void Ready(McpReadyInfo info)
{
string message = info.Transport == "http"
? _locale.Format("app.ready.http", new Dictionary<string, object?>
{
["endpoint"] = info.Endpoint, ["protocol"] = info.ProtocolVersion, ["stateless"] = info.Stateless.ToString().ToLowerInvariant(),
["fallback"] = info.LegacyFallback.ToString().ToLowerInvariant(), ["pid"] = info.ProcessId, ["logs"] = _logDirectory
})
: _locale.Format("app.ready.stdio", new Dictionary<string, object?>
{
["protocol"] = info.ProtocolVersion, ["pid"] = info.ProcessId, ["logs"] = _logDirectory
});
_application.Operational(message);
}
public void HttpRequestStarted(McpHttpRequestStartInfo info)
{
if (_observedConnections.TryAdd(info.ConnectionId, 0))
_application.Information($"HTTP connection observed connection={info.ConnectionId} remote={info.RemoteIp}:{info.RemotePort}");
_application.Information($"HTTP request started connection={info.ConnectionId} trace={info.TraceId} remote={info.RemoteIp}:{info.RemotePort} method={info.Method} path={info.Path}");
}
public void HttpRequest(McpHttpRequestInfo info)
{
_application.Information(string.Create(CultureInfo.InvariantCulture,
$"HTTP request completed connection={info.ConnectionId} remote={info.RemoteIp}:{info.RemotePort} method={info.Method} path={info.Path} status={info.Status} duration_ms={info.DurationMs:F3}"));
_http?.WriteLine(JsonSerializer.Serialize(new
{
timestamp = info.Timestamp,
connection_id = info.ConnectionId,
trace_id = info.TraceId,
remote_ip = info.RemoteIp,
remote_port = info.RemotePort,
local_ip = info.LocalIp,
local_port = info.LocalPort,
method = info.Method,
path = info.Path,
status = info.Status,
duration_ms = info.DurationMs,
content_type = info.ContentType,
content_length = info.ContentLength,
user_agent = info.UserAgent,
mcp_protocol_version = NullIfEmpty(info.ProtocolVersion),
mcp_method = NullIfEmpty(info.McpMethod),
mcp_name = NullIfEmpty(info.McpName)
}));
}
private static string? NullIfEmpty(string? value) => string.IsNullOrWhiteSpace(value) ? null : value;
public void Dispose() => _http?.Dispose();
}
|