fastmcp_console/
detection.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum DisplayContext {
8 Agent,
10 #[default]
12 Human,
13}
14
15impl DisplayContext {
16 #[must_use]
18 pub fn new_agent() -> Self {
19 Self::Agent
20 }
21
22 #[must_use]
24 pub fn new_human() -> Self {
25 Self::Human
26 }
27
28 #[must_use]
30 pub fn detect() -> Self {
31 if should_enable_rich() {
32 Self::Human
33 } else {
34 Self::Agent
35 }
36 }
37
38 #[must_use]
40 pub fn is_human(&self) -> bool {
41 matches!(self, Self::Human)
42 }
43
44 #[must_use]
46 pub fn is_agent(&self) -> bool {
47 matches!(self, Self::Agent)
48 }
49}
50
51#[must_use]
53pub fn is_agent_context() -> bool {
54 std::env::var("MCP_CLIENT").is_ok()
56 || std::env::var("CLAUDE_CODE").is_ok()
57 || std::env::var("CODEX_CLI").is_ok()
58 || std::env::var("CURSOR_SESSION").is_ok()
59 || std::env::var("CI").is_ok()
61 || std::env::var("AGENT_MODE").is_ok()
62 || std::env::var("FASTMCP_PLAIN").is_ok()
64 || std::env::var("NO_COLOR").is_ok()
65}
66
67#[must_use]
69pub fn should_enable_rich() -> bool {
70 use std::io::IsTerminal;
71
72 if std::env::var("FASTMCP_PLAIN").is_ok() || std::env::var("NO_COLOR").is_ok() {
75 return false;
76 }
77
78 if std::env::var("FASTMCP_RICH").is_ok() || std::env::var("FASTMCP_FORCE_COLOR").is_ok() {
80 return true;
81 }
82
83 if is_agent_context() {
85 return false;
86 }
87
88 std::io::stderr().is_terminal()
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_display_context_new_agent() {
98 let ctx = DisplayContext::new_agent();
99 assert!(ctx.is_agent());
100 assert!(!ctx.is_human());
101 }
102
103 #[test]
104 fn test_display_context_new_human() {
105 let ctx = DisplayContext::new_human();
106 assert!(ctx.is_human());
107 assert!(!ctx.is_agent());
108 }
109
110 #[test]
111 fn test_display_context_default_is_human() {
112 let ctx = DisplayContext::default();
113 assert!(ctx.is_human());
114 }
115
116 #[test]
117 fn test_display_context_equality() {
118 assert_eq!(DisplayContext::Agent, DisplayContext::Agent);
119 assert_eq!(DisplayContext::Human, DisplayContext::Human);
120 assert_ne!(DisplayContext::Agent, DisplayContext::Human);
121 }
122
123 #[test]
124 fn test_display_context_clone() {
125 let ctx = DisplayContext::Agent;
126 let cloned = ctx;
127 assert_eq!(ctx, cloned);
128 }
129
130 #[test]
131 fn test_display_context_debug() {
132 let ctx = DisplayContext::Agent;
133 let debug_str = format!("{:?}", ctx);
134 assert!(debug_str.contains("Agent"));
135 }
136
137 #[test]
142 fn display_context_copy_semantics() {
143 let ctx = DisplayContext::Agent;
144 let copied = ctx;
145 assert!(ctx.is_agent());
147 assert!(copied.is_agent());
148 }
149
150 #[test]
151 fn display_context_debug_human() {
152 let ctx = DisplayContext::Human;
153 let debug_str = format!("{ctx:?}");
154 assert!(debug_str.contains("Human"));
155 }
156
157 #[test]
158 fn detect_returns_valid_context() {
159 let ctx = DisplayContext::detect();
161 assert!(ctx.is_agent() || ctx.is_human());
162 }
163
164 #[test]
165 fn is_agent_context_and_should_enable_rich_are_consistent() {
166 if is_agent_context() {
169 let force_plain =
170 std::env::var("FASTMCP_PLAIN").is_ok() || std::env::var("NO_COLOR").is_ok();
171 let force_rich = std::env::var("FASTMCP_RICH").is_ok()
172 || std::env::var("FASTMCP_FORCE_COLOR").is_ok();
173 if force_plain || !force_rich {
174 assert!(!should_enable_rich());
175 }
176 }
177 }
178}