Skip to main content

soma_mcp_server/
trace.rs

1//! Reusable trace metadata extraction, integrating `rmcp-traces` for inbound
2//! RMCP request `_meta`.
3//!
4//! This module owns the generic, protocol-level extraction step only. Turning
5//! the recovered fields into a product-specific trace-context type (with its
6//! own field names, propagation policy, and downstream plumbing) is a product
7//! adapter concern and stays out of this crate.
8
9use rmcp::model::Meta;
10
11pub use rmcp_traces::{TraceSummary, TraceTrust};
12
13/// Build a bounded [`TraceSummary`] from inbound RMCP request `_meta`.
14///
15/// This is a thin wrapper so MCP server implementations get discoverable,
16/// documented trace extraction from `soma-mcp-server` without adding a direct
17/// `rmcp-traces` dependency themselves.
18pub fn trace_summary_from_meta(meta: &Meta, trust: TraceTrust) -> TraceSummary {
19    TraceSummary::from_meta(meta, trust)
20}
21
22/// Raw W3C `traceparent`/`tracestate` header values recovered from `_meta`.
23#[derive(Clone, Debug, Default, PartialEq, Eq)]
24pub struct RawTraceFields {
25    pub traceparent: Option<String>,
26    pub tracestate: Option<String>,
27}
28
29/// One bounded parse of request `_meta`, keeping the validated summary and raw
30/// fields tied to the same input.
31#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct TraceMetaExtraction {
33    pub summary: TraceSummary,
34    pub raw_fields: Option<RawTraceFields>,
35}
36
37/// Parse request `_meta` once and derive both safe summary and gated raw fields.
38pub fn extract_trace_meta(meta: &Meta, trust: TraceTrust) -> TraceMetaExtraction {
39    let summary = trace_summary_from_meta(meta, trust);
40    let raw_fields = raw_trace_fields_from_summary(meta, &summary);
41    TraceMetaExtraction {
42        summary,
43        raw_fields,
44    }
45}
46
47/// Recover raw `traceparent`/`tracestate` values from `_meta`, gated on the
48/// bounded [`TraceSummary`] confirming a validated trace id. Returns `None`
49/// when no valid trace id was extracted (absent, malformed, or over budget).
50pub fn raw_trace_fields_from_meta(meta: &Meta, trust: TraceTrust) -> Option<RawTraceFields> {
51    extract_trace_meta(meta, trust).raw_fields
52}
53
54fn raw_trace_fields_from_summary(meta: &Meta, summary: &TraceSummary) -> Option<RawTraceFields> {
55    summary.trace_id_prefix()?;
56    Some(RawTraceFields {
57        traceparent: meta.get_traceparent().map(ToOwned::to_owned),
58        tracestate: summary
59            .has_tracestate()
60            .then(|| meta.get_tracestate().map(ToOwned::to_owned))
61            .flatten(),
62    })
63}
64
65#[cfg(test)]
66#[path = "trace_tests.rs"]
67mod tests;