Reuse the UTF-8 boundary helper for the stderr tail

- Skip a partial character at the start of the retained stderr tail with the shared boundary helper, and test tails that begin inside a character.
- Fail the stdio diagnostics tests instead of crashing when a child has not been reaped.
- Rewrap the startup failure paragraph in CONTRIBUTING.md.
This commit is contained in:
Pranit committed 2026-09-23 23:15:51 -04:00
1 parent 91f19ac714
commit f97b2b80c1
3 files changed
+21 -11

No files matched your search

+7 -6
View File
@@ -310,12 +310,13 @@ after shutdown or startup failure. An explicit cidfile remains user-owned.
When a stdio server closes its connection before answering `initialize`, for
example because its process exited, the reported failure names the exit code
or signal and includes a bounded, terminal-safe excerpt of the server's stderr
with secrets masked. A startup timeout names the budget the
connection had, plus an earlier launch's exit when there was one, and names the
`startup_timeout_ms` key when that setting set the budget. A startup restart runs only when it could change the
outcome: a server that closed its connection at every offered protocol version
is not restarted, and neither is one whose startup deadline has already passed.
A server that fx stopped because of invalid output still gets its restart.
with secrets masked. A startup timeout names the budget the connection had,
plus an earlier launch's exit when there was one, and names the
`startup_timeout_ms` key when that setting set the budget. A startup restart
runs only when it could change the outcome: a server that closed its
connection at every offered protocol version is not restarted, and neither is
one whose startup deadline has already passed. A server that fx stopped
because of invalid output still gets its restart.
MongoDB Atlas Managed MCP configuration service accounts use the OAuth
client-credentials grant. fx does not implement that grant directly. Use
+12 -3
View File
@@ -1263,9 +1263,7 @@ fn displayStderr(arena: Allocator, capture: *const stdio_dispatcher.StderrCaptur
try plain.appendSlice(arena, " ... ");
// The retained tail can begin inside a UTF-8 sequence.
const tail = capture.tailSlice();
var tail_start: usize = 0;
while (tail_start < tail.len and (tail[tail_start] & 0xc0) == 0x80) tail_start += 1;
try appendWithoutAnsi(arena, &plain, tail[tail_start..]);
try appendWithoutAnsi(arena, &plain, tail[text_utils.utf8ForwardBoundary(tail, 0)..]);
} else {
const contiguous = try std.mem.concat(arena, u8, &.{ capture.headSlice(), capture.tailSlice() });
try appendWithoutAnsi(arena, &plain, contiguous);
@@ -1394,6 +1392,17 @@ test "server stderr display keeps the first line and the end, bounded and masked
hostile.append("\x98\xa9ok \xff caf\xc3\xa9\x1b]0;title\x07\n\n\t done\x07");
try std.testing.expectEqualStrings("\\x98\\xa9ok \\xff caf\xc3\xa9 done\\x07", try displayStderr(arena, &hostile));
// One of these makes the retained tail start inside a two-byte character.
for ([_][]const u8{ "z", "zz" }) |suffix| {
var split: stdio_dispatcher.StderrCapture = .{};
for (0..3_000) |_| split.append("\xc3\xa9");
split.append(suffix);
try std.testing.expect(split.omitted);
const shown = try displayStderr(arena, &split);
try std.testing.expect(std.mem.find(u8, shown, "\\x") == null);
try std.testing.expect(std.mem.endsWith(u8, shown, suffix));
}
var invisible: stdio_dispatcher.StderrCapture = .{};
invisible.append("rtl \u{202e}txt\u{200b} nel\u{85}");
try std.testing.expectEqualStrings("rtl \\u{202e}txt\\u{200b} nel\\u{0085}", try displayStderr(arena, &invisible));
+2 -2
View File
@@ -2665,7 +2665,7 @@ test "MCP stdio records how a child that exits before replying ended and what it
),
);
const diagnostics = dispatcher.childDiagnostics();
try std.testing.expectEqual(std.process.Child.Term{ .exited = 3 }, diagnostics.term.?);
try std.testing.expectEqual(std.process.Child.Term{ .exited = 3 }, diagnostics.term orelse return error.TestExpectedExit);
try std.testing.expectEqualStrings(
"npm error code E401\nnpm error Incorrect or missing password.\n",
diagnostics.stderr.headSlice(),
@@ -2703,7 +2703,7 @@ test "MCP stdio reports a write to a child that closed stdin as a closed connect
);
// fx ends a child it can no longer write to; the details still arrive.
const diagnostics = dispatcher.childDiagnostics();
try std.testing.expectEqual(std.process.Child.Term{ .signal = .KILL }, diagnostics.term.?);
try std.testing.expectEqual(std.process.Child.Term{ .signal = .KILL }, diagnostics.term orelse return error.TestExpectedExit);
try std.testing.expectEqualStrings("stdin closed\n", diagnostics.stderr.headSlice());
}