protocol_droid/application/registry/mod.rs
1//! The use case: a set of tools, resources, templates and prompts that changes
2//! while sessions are using it.
3//!
4//! # The three concerns, and which file owns each
5//!
6//! | File | Concern |
7//! |---|---|
8//! | [`store`] | The publish. Copy the live snapshot, change one thing, swap. |
9//! | [`subscriber`] | The fan-out, gated on each session's negotiated profile. |
10//! | [`listing`] | The read: one snapshot filtered for one session. |
11//!
12//! The split is the same one T10's engine makes. A use case is a sequence of
13//! steps, and each step that could be described on its own gets a file, rather
14//! than three methods on one type that a reader has to hold in their head
15//! together.
16//!
17//! # What the swap means for a running tool
18//!
19//! An in-flight `tools/call` completes against the snapshot it resolved
20//! against, even if the tool is unregistered a microsecond later. Nothing here
21//! can change that: the reader holds an owned `Arc<Snapshot>`, so there is no
22//! borrow for a publish to wait on, and blocking a publish until every running
23//! tool finished would let one slow tool stall every registration on the
24//! server. The alternative — cancelling in-flight calls on unregister — is a
25//! policy question, and it is not this module's to answer.
26//!
27//! # Where the protocol lives
28//!
29//! Nothing here names a method, a notification, or a revision. The
30//! [`subscriber::Subscriber`] is given a [`Feature`](crate::domain::Feature)
31//! and a method name by whoever wires the server up, because which
32//! notification means "the tool list changed" and which capability has to be
33//! advertised for it to be welcome are facts about a protocol, and this module
34//! is the part that would have to work for A2A as well as for MCP.
35//!
36//! # Examples
37//!
38//! ```
39//! use protocol_droid::application::registry::RegistryStore;
40//! use protocol_droid::domain::registry::{AuthContext, Primitive, PrimitiveKind, Tool};
41//! use protocol_droid::domain::FeatureProfile;
42//!
43//! let store = RegistryStore::new();
44//! store.register(Primitive::from(Tool::new("echo", r#"{"type":"object"}"#)?));
45//!
46//! let session = AuthContext::unrestricted("alice");
47//! let listing = store.list_visible(&session, &FeatureProfile::all(), PrimitiveKind::Tool);
48//!
49//! assert_eq!(listing.visible().len(), 1);
50//! assert_eq!(listing.hidden(), 0);
51//! # Ok::<(), Box<dyn std::error::Error>>(())
52//! ```
53
54pub mod listing;
55pub mod store;
56pub mod subscriber;
57
58pub use listing::Listing;
59pub use store::{Published, RegistryStore};
60pub use subscriber::{Fanout, Subscriber, SubscriberSet};