Skip to main content

ConfigFingerprint

Struct ConfigFingerprint 

Source
pub struct ConfigFingerprint(/* private fields */);
Expand description

A stable fingerprint of the ServerConfig used to connect to and introspect a server, recorded so a later comparison can detect that connection parameters changed.

Newtype over a 64-character lowercase-hex String (a SHA-256 digest), rather than a bare String, for the same reason crate::ServerId/crate::ToolName are newtypes: two same-shaped hex strings sitting adjacent in crate::metadata::GenerationProvenance are trivially swappable by accident, and a single-field newtype still serializes transparently as a JSON string. Deserialize is routed through TryFrom<String> (via #[serde(try_from = "String")]), so a value read back from disk is validated the same way crate::ServerId/crate::ToolName are — see DigestFormatError.

§Examples

use mcp_execution_core::provenance::ConfigFingerprint;
use mcp_execution_core::ServerConfig;

let config = ServerConfig::builder().command("docker".to_string()).build().unwrap();
let fingerprint = ConfigFingerprint::compute(&config);
assert_eq!(fingerprint.as_str().len(), 64);
assert!(fingerprint.as_str().chars().all(|c| c.is_ascii_hexdigit()));

Implementations§

Source§

impl ConfigFingerprint

Source

pub fn compute(config: &ServerConfig) -> Self

Computes a fingerprint of config, sufficient to detect that connection parameters changed, without persisting any secret-bearing value.

The preimage carries, in a fixed order: a domain tag; the transport discriminant (stdio/http/sse); for stdio, command, cwd presence, argument count, and every environment variable name (sorted); for http/sse, the URL’s canonical scheme://authority/path form, its query-parameter names (deduplicated, sorted), a userinfo-present marker, and every header name (ASCII-lowercased, sorted). No argument value, environment/header value, query-parameter value, or userinfo is ever fed — see the module docs.

ServerConfig::connect_timeout/discover_timeout are deliberately excluded: they bound how long the client waits for a response, not what the server exposes, so changing one must not register as a change to the server’s identity or tool surface.

Residual collision, documented rather than fixed: every URL split_url cannot parse — including two configs whose only difference is inside the ambiguous userinfo case it rejects, e.g. https://u:p/w@a.com vs. https://u:p/w@b.com — collapses onto the same URL_UNPARSEABLE marker and therefore the same fingerprint. This is the same class of secrecy-over-precision tradeoff as the other residual collisions in this family (query values, userinfo credentials): the input a real fingerprint would need to distinguish them is exactly the text this function refuses to hash.

§Examples

Configs differing only in secret-bearing values fingerprint identically:

use mcp_execution_core::provenance::ConfigFingerprint;
use mcp_execution_core::ServerConfig;

let a = ServerConfig::builder()
    .command("docker".to_string())
    .env("TOKEN".to_string(), "secret-a".to_string())
    .build()
    .unwrap();
let b = ServerConfig::builder()
    .command("docker".to_string())
    .env("TOKEN".to_string(), "secret-b".to_string())
    .build()
    .unwrap();

assert_eq!(ConfigFingerprint::compute(&a), ConfigFingerprint::compute(&b));
Source

pub fn as_str(&self) -> &str

Returns the fingerprint as a 64-character lowercase-hex string slice.

§Examples
use mcp_execution_core::provenance::ConfigFingerprint;
use mcp_execution_core::ServerConfig;

let config = ServerConfig::builder().command("docker".to_string()).build().unwrap();
let fingerprint = ConfigFingerprint::compute(&config);
assert!(!fingerprint.as_str().is_empty());

Trait Implementations§

Source§

impl Clone for ConfigFingerprint

Source§

fn clone(&self) -> ConfigFingerprint

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConfigFingerprint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for ConfigFingerprint

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for ConfigFingerprint

Source§

impl PartialEq for ConfigFingerprint

Source§

fn eq(&self, other: &ConfigFingerprint) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for ConfigFingerprint

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for ConfigFingerprint

Source§

impl TryFrom<String> for ConfigFingerprint

Source§

fn try_from(value: String) -> Result<Self, Self::Error>

Delegates to validate_digest_string — the sole entry point Deserialize uses.

Source§

type Error = DigestFormatError

The type returned in the event of a conversion error.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.