@@ -21,6 +21,7 @@ use tracing::debug;
2121use tracing:: error;
2222use tracing:: info;
2323use tracing_subscriber:: EnvFilter ;
24+ use tracing_subscriber:: prelude:: * ;
2425
2526mod codex_tool_config;
2627mod codex_tool_runner;
@@ -45,19 +46,53 @@ pub use crate::patch_approval::PatchApprovalResponse;
4546/// is a balance between throughput and memory usage – 128 messages should be
4647/// plenty for an interactive CLI.
4748const CHANNEL_CAPACITY : usize = 128 ;
49+ const DEFAULT_ANALYTICS_ENABLED : bool = true ;
50+ const OTEL_SERVICE_NAME : & str = "codex_mcp_server" ;
4851
4952type IncomingMessage = JsonRpcMessage < ClientRequest , Value , ClientNotification > ;
5053
5154pub async fn run_main (
5255 arg0_paths : Arg0DispatchPaths ,
5356 cli_config_overrides : CliConfigOverrides ,
5457) -> IoResult < ( ) > {
55- // Install a simple subscriber so `tracing` output is visible. Users can
56- // control the log level with `RUST_LOG`.
57- tracing_subscriber:: fmt ( )
58+ // Parse CLI overrides once and derive the base Config eagerly so later
59+ // components do not need to work with raw TOML values.
60+ let cli_kv_overrides = cli_config_overrides. parse_overrides ( ) . map_err ( |e| {
61+ std:: io:: Error :: new (
62+ ErrorKind :: InvalidInput ,
63+ format ! ( "error parsing -c overrides: {e}" ) ,
64+ )
65+ } ) ?;
66+ let config = Config :: load_with_cli_overrides ( cli_kv_overrides)
67+ . await
68+ . map_err ( |e| {
69+ std:: io:: Error :: new ( ErrorKind :: InvalidData , format ! ( "error loading config: {e}" ) )
70+ } ) ?;
71+
72+ let otel = codex_core:: otel_init:: build_provider (
73+ & config,
74+ env ! ( "CARGO_PKG_VERSION" ) ,
75+ Some ( OTEL_SERVICE_NAME ) ,
76+ DEFAULT_ANALYTICS_ENABLED ,
77+ )
78+ . map_err ( |e| {
79+ std:: io:: Error :: new (
80+ ErrorKind :: InvalidData ,
81+ format ! ( "error loading otel config: {e}" ) ,
82+ )
83+ } ) ?;
84+
85+ let fmt_layer = tracing_subscriber:: fmt:: layer ( )
5886 . with_writer ( std:: io:: stderr)
59- . with_env_filter ( EnvFilter :: from_default_env ( ) )
60- . init ( ) ;
87+ . with_filter ( EnvFilter :: from_default_env ( ) ) ;
88+ let otel_logger_layer = otel. as_ref ( ) . and_then ( |provider| provider. logger_layer ( ) ) ;
89+ let otel_tracing_layer = otel. as_ref ( ) . and_then ( |provider| provider. tracing_layer ( ) ) ;
90+
91+ let _ = tracing_subscriber:: registry ( )
92+ . with ( fmt_layer)
93+ . with ( otel_logger_layer)
94+ . with ( otel_tracing_layer)
95+ . try_init ( ) ;
6196
6297 // Set up channels.
6398 let ( incoming_tx, mut incoming_rx) = mpsc:: channel :: < IncomingMessage > ( CHANNEL_CAPACITY ) ;
@@ -86,20 +121,6 @@ pub async fn run_main(
86121 }
87122 } ) ;
88123
89- // Parse CLI overrides once and derive the base Config eagerly so later
90- // components do not need to work with raw TOML values.
91- let cli_kv_overrides = cli_config_overrides. parse_overrides ( ) . map_err ( |e| {
92- std:: io:: Error :: new (
93- ErrorKind :: InvalidInput ,
94- format ! ( "error parsing -c overrides: {e}" ) ,
95- )
96- } ) ?;
97- let config = Config :: load_with_cli_overrides ( cli_kv_overrides)
98- . await
99- . map_err ( |e| {
100- std:: io:: Error :: new ( ErrorKind :: InvalidData , format ! ( "error loading config: {e}" ) )
101- } ) ?;
102-
103124 // Task: process incoming messages.
104125 let processor_handle = tokio:: spawn ( {
105126 let outgoing_message_sender = OutgoingMessageSender :: new ( outgoing_tx) ;
@@ -152,3 +173,55 @@ pub async fn run_main(
152173
153174 Ok ( ( ) )
154175}
176+
177+ #[ cfg( test) ]
178+ mod tests {
179+ use super :: * ;
180+ use codex_core:: config:: ConfigBuilder ;
181+ use codex_core:: config:: types:: OtelExporterKind ;
182+ use pretty_assertions:: assert_eq;
183+ use std:: collections:: HashMap ;
184+ use tempfile:: TempDir ;
185+
186+ #[ test]
187+ fn mcp_server_defaults_analytics_to_enabled ( ) {
188+ assert_eq ! ( DEFAULT_ANALYTICS_ENABLED , true ) ;
189+ }
190+
191+ #[ tokio:: test]
192+ async fn mcp_server_builds_otel_provider_with_logs_traces_and_metrics ( ) -> anyhow:: Result < ( ) > {
193+ let codex_home = TempDir :: new ( ) ?;
194+ let mut config = ConfigBuilder :: default ( )
195+ . codex_home ( codex_home. path ( ) . to_path_buf ( ) )
196+ . build ( )
197+ . await ?;
198+ let exporter = OtelExporterKind :: OtlpGrpc {
199+ endpoint : "http://localhost:4317" . to_string ( ) ,
200+ headers : HashMap :: new ( ) ,
201+ tls : None ,
202+ } ;
203+ config. otel . exporter = exporter. clone ( ) ;
204+ config. otel . trace_exporter = exporter. clone ( ) ;
205+ config. otel . metrics_exporter = exporter;
206+ config. analytics_enabled = None ;
207+
208+ let provider = codex_core:: otel_init:: build_provider (
209+ & config,
210+ "0.0.0-test" ,
211+ Some ( OTEL_SERVICE_NAME ) ,
212+ DEFAULT_ANALYTICS_ENABLED ,
213+ )
214+ . map_err ( |err| anyhow:: anyhow!( err. to_string( ) ) ) ?
215+ . expect ( "otel provider" ) ;
216+
217+ assert ! ( provider. logger. is_some( ) , "expected log exporter" ) ;
218+ assert ! (
219+ provider. tracer_provider. is_some( ) ,
220+ "expected trace exporter"
221+ ) ;
222+ assert ! ( provider. metrics( ) . is_some( ) , "expected metrics exporter" ) ;
223+ provider. shutdown ( ) ;
224+
225+ Ok ( ( ) )
226+ }
227+ }
0 commit comments