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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! Stdout-redirect dance for the MCP transport.
//!
//! ## Why this exists
//!
//! The MCP wire format on stdio is JSON-RPC 2.0, one message per line
//! on stdout. ANY other write to stdout corrupts the protocol — a
//! lone `println!` from somewhere in the dep tree, a debug print left
//! in by accident, a banner from a third-party crate. The MCP client
//! sees garbage where it expected JSON and disconnects.
//!
//! The SQLRite engine's REPL-convenience prints inside
//! `process_command` (the `CREATE TABLE` schema dump at sql/mod.rs:150,
//! the `INSERT` row dump at sql/mod.rs:208, the `SELECT` result table
//! at sql/mod.rs:224) are exactly this kind of write. They're great
//! for an interactive REPL; lethal for an MCP server.
//!
//! ## What this module does
//!
//! At process startup, before opening the database:
//!
//! 1. Duplicate fd 1 (stdout) to get a private handle pointing at
//! the original destination — that's where MCP responses go.
//! 2. `dup2(2, 1)` — overwrite fd 1 with stderr, so any subsequent
//! `print!` / `println!` from anywhere in the process ends up on
//! stderr (visible in the MCP client's "server log" pane).
//! 3. Hand back a `File` wrapping the saved-off fd; the transport
//! runner writes JSON-RPC responses to it.
//!
//! ## Cross-platform notes
//!
//! `libc::dup` / `libc::dup2` exist on both Unix and Windows (Windows
//! targets resolve them to `_dup` / `_dup2` in the MSVCRT). The
//! divergence is in turning the resulting C runtime fd into a Rust
//! `File`:
//!
//! - **Unix**: `std::os::fd::FromRawFd::from_raw_fd(fd) -> File`
//! directly. The fd IS the kernel handle.
//! - **Windows**: C runtime fds aren't kernel handles. We need
//! `_get_osfhandle(fd) -> HANDLE`, then
//! `std::os::windows::io::FromRawHandle::from_raw_handle(handle) -> File`.
//!
//! Two `#[cfg]` arms below.
use File;
use io;
/// Replace process fd 1 (stdout) with a duplicate of fd 2 (stderr),
/// returning a `File` that still writes to the original stdout. Call
/// this exactly once, very early in `main`, before anything else can
/// emit stdout.
///
/// Returns an error if either `dup` or `dup2` fails — both extremely
/// rare (out of file descriptors, somebody closed fd 1 already, etc.)
/// and worth aborting on.