- redact /mcp output, and stderr lines as they arrive (log, errors, tail)
- take host servers by name: configuration binding sorts keys anyway
- dispose kills the tree only without an exit code and only if the pid's
start time matches the spawn (no kill of a reused pid)
- idle timer generation guards stale callbacks; catalogue check runs in
the background with a 10 s cap; shutdown stops servers concurrently
- stop resets a failed status; /mcp shows an exited process; ordinal names
- spec: ${workspace} in command, ordering, dispose and /mcp wording
28 lines
946 B
C#
28 lines
946 B
C#
namespace Volcanic.AgenticCore.Extensions.Mcp;
|
|
|
|
/// <summary>
|
|
/// The last lines a server wrote to stderr: a server that dies explains why only there. Lines are redacted as they
|
|
/// arrive, so the log, error messages and <c>/mcp</c> built from them never hold a known secret.
|
|
/// </summary>
|
|
internal sealed class StderrTail(Func<string, string>? redact = null, int capacity = 20)
|
|
{
|
|
private readonly Queue<string> _lines = new();
|
|
private readonly Lock _lock = new();
|
|
|
|
/// <summary>Stores the redacted line and returns it.</summary>
|
|
public string Add(string line)
|
|
{
|
|
if (redact is not null) line = redact(line);
|
|
lock (_lock)
|
|
{
|
|
_lines.Enqueue(line);
|
|
while (_lines.Count > capacity) _lines.Dequeue();
|
|
}
|
|
return line;
|
|
}
|
|
|
|
public void Clear() { lock (_lock) _lines.Clear(); }
|
|
|
|
public IReadOnlyList<string> Lines { get { lock (_lock) return [.. _lines]; } }
|
|
}
|