pub struct AgentUri { /* private fields */ }Expand description
A parsed and validated agent URI.
Agent URIs provide topology-independent identity for agents with capability-based discovery.
§Structure
agent://<trust-root>/<capability-path>/<agent-id>[?query][#fragment]§Examples
use agent_uri::AgentUri;
let uri = AgentUri::parse("agent://anthropic.com/assistant/chat/llm_chat_01h455vb4pex5vsknk084sn02q").unwrap();
assert_eq!(uri.trust_root().host_str(), "anthropic.com");
assert_eq!(uri.capability_path().as_str(), "assistant/chat");
assert_eq!(uri.agent_id().prefix().as_str(), "llm_chat");
// With query and fragment
let uri = AgentUri::parse("agent://openai.com/tool/code/llm_01h455vb4pex5vsknk084sn02q?version=2.0#summarization").unwrap();
assert_eq!(uri.query().version(), Some("2.0"));
assert_eq!(uri.fragment().map(|f| f.as_str()), Some("summarization"));§Identity
Two AgentUri values are equal when they name the same agent, which is a
weaker condition than being the same string. Equality, ordering, and
hashing compare the trust root, the capability path, and the agent ID —
the canonical form. The query and the fragment are excluded: a query
carries resolution hints and a fragment names a sub-capability, and neither
changes which agent is being addressed. This is Section 5.2 of the
specification, and it is what makes ?version=2.0 a request about an
agent rather than a different agent.
A value that is not part of identity is still part of the string.
Display, as_str, and serde all
preserve the query and the fragment exactly as parsed. Only
canonical drops them.
use agent_uri::AgentUri;
let plain = AgentUri::parse(
"agent://acme.com/workflow/approval/rule_01h455vb4pex5vsknk084sn02q"
).unwrap();
let hinted = AgentUri::parse(
"agent://acme.com/workflow/approval/rule_01h455vb4pex5vsknk084sn02q?ttl=300#invoice"
).unwrap();
// Equal as identities...
assert_eq!(plain, hinted);
assert_eq!(plain.canonical(), hinted.canonical());
// ...and not as strings.
assert_ne!(plain.as_str(), hinted.as_str());
assert_eq!(hinted.query().ttl(), Some(300));
assert_eq!(hinted.fragment().map(|f| f.as_str()), Some("invoice"));§What this means for collections
A HashSet or HashMap keyed by AgentUri deduplicates by identity, so
inserting a decorated URI where an undecorated one is already present keeps
the first and discards the second — including its query and fragment. That
is the intent for caches, registries, and DHT keys, where one agent must
have one entry. It is a defect if the query is data you meant to keep, so
key those by as_str instead.
use std::collections::HashSet;
use agent_uri::AgentUri;
let plain = AgentUri::parse(
"agent://acme.com/workflow/approval/rule_01h455vb4pex5vsknk084sn02q"
).unwrap();
let hinted = AgentUri::parse(
"agent://acme.com/workflow/approval/rule_01h455vb4pex5vsknk084sn02q?ttl=300"
).unwrap();
let mut agents = HashSet::new();
assert!(agents.insert(plain));
assert!(!agents.insert(hinted)); // same agent, so not a second entry
assert_eq!(agents.len(), 1);Implementations§
Source§impl AgentUri
impl AgentUri
Sourcepub fn parse(input: &str) -> Result<Self, ParseError>
pub fn parse(input: &str) -> Result<Self, ParseError>
Parses an agent URI from a string.
§Case
Case is folded exactly where a standard makes a component case-insensitive: the scheme (RFC 3986 section 3.1) and the trust root’s DNS name (RFC 4343). The capability path and the agent ID are identity material that the grammar fixes as lowercase, so an uppercase letter in either is rejected rather than folded.
use agent_uri::AgentUri;
let uri = AgentUri::parse("AGENT://Anthropic.COM/chat/llm_01h455vb4pex5vsknk084sn02q").unwrap();
assert_eq!(
uri.as_str(),
"agent://anthropic.com/chat/llm_01h455vb4pex5vsknk084sn02q"
);
assert!(AgentUri::parse("agent://anthropic.com/Chat/llm_01h455vb4pex5vsknk084sn02q").is_err());§Errors
Returns ParseError if:
- The URI is empty
- The URI exceeds 512 characters
- The scheme is not “agent” (compared case-insensitively)
- Any component (trust root, path, agent ID, query, fragment) is invalid
Sourcepub fn new(
trust_root: TrustRoot,
capability_path: CapabilityPath,
agent_id: AgentId,
query: QueryParams,
fragment: Option<Fragment>,
) -> Result<Self, ParseError>
pub fn new( trust_root: TrustRoot, capability_path: CapabilityPath, agent_id: AgentId, query: QueryParams, fragment: Option<Fragment>, ) -> Result<Self, ParseError>
Creates a new agent URI from its components.
§Errors
Returns ParseError if the resulting URI would exceed the maximum length.
Sourcepub const fn trust_root(&self) -> &TrustRoot
pub const fn trust_root(&self) -> &TrustRoot
Returns the trust root.
Sourcepub const fn capability_path(&self) -> &CapabilityPath
pub const fn capability_path(&self) -> &CapabilityPath
Returns the capability path.
Sourcepub const fn query(&self) -> &QueryParams
pub const fn query(&self) -> &QueryParams
Returns the query parameters.
Sourcepub fn is_localhost(&self) -> bool
pub fn is_localhost(&self) -> bool
Returns true if this URI references a localhost agent.
Sourcepub fn with_query(&self, query: QueryParams) -> Result<Self, ParseError>
pub fn with_query(&self, query: QueryParams) -> Result<Self, ParseError>
Returns a new URI with the given query parameters.
§Errors
Returns ParseError if the resulting URI would exceed the maximum length.
§Examples
use agent_uri::{AgentUri, QueryParams};
let uri = AgentUri::parse("agent://anthropic.com/chat/llm_01h455vb4pex5vsknk084sn02q").unwrap();
let query = QueryParams::parse("version=2.0").unwrap();
let updated = uri.with_query(query).unwrap();
assert_eq!(updated.query().version(), Some("2.0"));Sourcepub fn with_query_str(&self, s: &str) -> Result<Self, ParseError>
pub fn with_query_str(&self, s: &str) -> Result<Self, ParseError>
Returns a new URI with query parameters parsed from a string.
§Errors
Returns ParseError if the query string is invalid or the resulting URI
would exceed the maximum length.
§Examples
use agent_uri::AgentUri;
let uri = AgentUri::parse("agent://anthropic.com/chat/llm_01h455vb4pex5vsknk084sn02q").unwrap();
let updated = uri.with_query_str("version=2.0&ttl=300").unwrap();
assert_eq!(updated.query().version(), Some("2.0"));
assert_eq!(updated.query().ttl(), Some(300));Sourcepub fn without_query(&self) -> Result<Self, ParseError>
pub fn without_query(&self) -> Result<Self, ParseError>
Returns a new URI without query parameters.
§Errors
Returns ParseError if the resulting URI would exceed the maximum length.
(This is unlikely but possible in edge cases.)
§Examples
use agent_uri::AgentUri;
let uri = AgentUri::parse("agent://anthropic.com/chat/llm_01h455vb4pex5vsknk084sn02q?version=2.0").unwrap();
let updated = uri.without_query().unwrap();
assert!(updated.query().is_empty());Sourcepub fn with_fragment(&self, fragment: Fragment) -> Result<Self, ParseError>
pub fn with_fragment(&self, fragment: Fragment) -> Result<Self, ParseError>
Returns a new URI with the given fragment.
§Errors
Returns ParseError if the resulting URI would exceed the maximum length.
§Examples
use agent_uri::{AgentUri, Fragment};
let uri = AgentUri::parse("agent://anthropic.com/chat/llm_01h455vb4pex5vsknk084sn02q").unwrap();
let fragment = Fragment::parse("summarization").unwrap();
let updated = uri.with_fragment(fragment).unwrap();
assert_eq!(updated.fragment().map(|f| f.as_str()), Some("summarization"));Sourcepub fn with_fragment_str(&self, s: &str) -> Result<Self, ParseError>
pub fn with_fragment_str(&self, s: &str) -> Result<Self, ParseError>
Returns a new URI with a fragment parsed from a string.
§Errors
Returns ParseError if the fragment is invalid or the resulting URI
would exceed the maximum length.
§Examples
use agent_uri::AgentUri;
let uri = AgentUri::parse("agent://anthropic.com/chat/llm_01h455vb4pex5vsknk084sn02q").unwrap();
let updated = uri.with_fragment_str("summarization").unwrap();
assert_eq!(updated.fragment().map(|f| f.as_str()), Some("summarization"));Sourcepub fn without_fragment(&self) -> Result<Self, ParseError>
pub fn without_fragment(&self) -> Result<Self, ParseError>
Returns a new URI without a fragment.
§Errors
Returns ParseError if the resulting URI would exceed the maximum length.
(This is unlikely but possible in edge cases.)
§Examples
use agent_uri::AgentUri;
let uri = AgentUri::parse("agent://anthropic.com/chat/llm_01h455vb4pex5vsknk084sn02q#test").unwrap();
let updated = uri.without_fragment().unwrap();
assert!(updated.fragment().is_none());Trait Implementations§
Source§impl<'de> Deserialize<'de> for AgentUri
Available on crate feature serde only.
impl<'de> Deserialize<'de> for AgentUri
serde only.Source§fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where
D: Deserializer<'de>,
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where
D: Deserializer<'de>,
impl Eq for AgentUri
Source§impl Hash for AgentUri
Hashes exactly what PartialEq compares, as the Hash/Eq contract
requires: equal URIs hash equally, so a decorated URI and its plain form
land in the same bucket rather than in two.
impl Hash for AgentUri
Hashes exactly what PartialEq compares, as the Hash/Eq contract
requires: equal URIs hash equally, so a decorated URI and its plain form
land in the same bucket rather than in two.
Source§impl Ord for AgentUri
impl Ord for AgentUri
This used to render both sides with canonical and
compare the strings, which allocated twice per comparison — a BTreeMap
keyed by AgentUri paid that on every probe. The order is the same except
where one component is a prefix of another, because the rendered form has a
/ between components and / sorts after . and after every letter:
string order put acme.com.br before acme.com, and this puts acme.com
first. Ordering by the components is the order the type’s own identity
implies; the old one was an artifact of the separator.
1.21.0 (const: unstable) · Source§fn max(self, other: Self) -> Selfwhere
Self: Sized,
fn max(self, other: Self) -> Selfwhere
Self: Sized,
1.21.0 (const: unstable) · Source§fn min(self, other: Self) -> Selfwhere
Self: Sized,
fn min(self, other: Self) -> Selfwhere
Self: Sized,
Source§impl PartialEq for AgentUri
Identity equality: see the Identity section on AgentUri.
impl PartialEq for AgentUri
Identity equality: see the Identity section on AgentUri.
Source§impl PartialOrd for AgentUri
impl PartialOrd for AgentUri
Auto Trait Implementations§
impl Freeze for AgentUri
impl RefUnwindSafe for AgentUri
impl Send for AgentUri
impl Sync for AgentUri
impl Unpin for AgentUri
impl UnsafeUnpin for AgentUri
impl UnwindSafe for AgentUri
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> PrefixFactory for T
impl<T> PrefixFactory for T
Source§fn create_prefix_sanitized(&self) -> TypeIdPrefix
fn create_prefix_sanitized(&self) -> TypeIdPrefix
TypeIdPrefix. Read moreSource§fn try_create_prefix(&self) -> Result<TypeIdPrefix, ValidationError>
fn try_create_prefix(&self) -> Result<TypeIdPrefix, ValidationError>
TypeIdPrefix from the input without modifying it. Read more