agentkit_capabilities/lib.rs
1//! Capability abstractions shared by tools, MCP servers, and agentkit hosts.
2//!
3//! This crate defines the [`Invocable`] trait and its supporting types, which
4//! let you expose arbitrary functionality (tools, resources, prompts) through a
5//! uniform interface that the agentkit loop can discover and call during a
6//! session.
7//!
8//! # Overview
9//!
10//! The core abstraction is [`Invocable`]: anything the model can call. Each
11//! invocable carries an [`InvocableSpec`] (name, description, JSON-schema for
12//! its input) and an async `invoke` method that receives an
13//! [`InvocableRequest`] and returns an [`InvocableResult`].
14//!
15//! Beyond direct invocation the crate also provides:
16//!
17//! * [`ResourceProvider`] -- lists and reads named data blobs (files, database
18//! rows, API responses) that the model can reference.
19//! * [`PromptProvider`] -- lists and renders parameterised prompt templates.
20//! * [`CapabilityProvider`] -- a bundle that groups invocables, resources, and
21//! prompts from a single source (e.g. an MCP server).
22//!
23//! All provider traits share a common [`CapabilityContext`] that carries the
24//! current session and turn identifiers, plus an open-ended metadata map.
25//!
26//! # Example
27//!
28//! ```rust
29//! use agentkit_capabilities::{
30//! CapabilityContext, CapabilityError, CapabilityName, Invocable,
31//! InvocableOutput, InvocableRequest, InvocableResult, InvocableSpec,
32//! };
33//! use async_trait::async_trait;
34//! use serde_json::json;
35//!
36//! /// A simple capability that echoes its input back to the model.
37//! struct Echo {
38//! spec: InvocableSpec,
39//! }
40//!
41//! impl Echo {
42//! fn new() -> Self {
43//! Self {
44//! spec: InvocableSpec::new(
45//! CapabilityName::new("echo"),
46//! "Return the input unchanged",
47//! json!({
48//! "type": "object",
49//! "properties": {
50//! "message": { "type": "string" }
51//! }
52//! }),
53//! ),
54//! }
55//! }
56//! }
57//!
58//! #[async_trait]
59//! impl Invocable for Echo {
60//! fn spec(&self) -> &InvocableSpec {
61//! &self.spec
62//! }
63//!
64//! async fn invoke(
65//! &self,
66//! request: InvocableRequest,
67//! _ctx: &mut CapabilityContext<'_>,
68//! ) -> Result<InvocableResult, CapabilityError> {
69//! Ok(InvocableResult::structured(request.input.clone()))
70//! }
71//! }
72//!
73//! let echo = Echo::new();
74//! assert_eq!(echo.spec().name.as_str(), "echo");
75//! ```
76
77use std::fmt;
78use std::sync::Arc;
79
80use agentkit_core::{DataRef, Item, MetadataMap, SessionId, TurnId};
81use async_trait::async_trait;
82use serde::{Deserialize, Serialize};
83use serde_json::Value;
84use thiserror::Error;
85
86macro_rules! capability_id {
87 ($name:ident, $doc:expr) => {
88 #[doc = $doc]
89 ///
90 /// This is a newtype wrapper around [`String`] that provides
91 /// type-safe identity within the capability system. It implements
92 /// [`Display`](fmt::Display), serialisation, ordering, and hashing.
93 ///
94 /// # Example
95 ///
96 /// ```rust
97 #[doc = concat!("use agentkit_capabilities::", stringify!($name), ";")]
98 ///
99 #[doc = concat!("let id = ", stringify!($name), "::new(\"my-id\");")]
100 #[doc = concat!("assert_eq!(id.as_str(), \"my-id\");")]
101 /// ```
102 #[derive(
103 Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
104 )]
105 pub struct $name(pub String);
106
107 impl $name {
108 /// Creates a new identifier from any value that can be converted
109 /// into a [`String`].
110 pub fn new(value: impl Into<String>) -> Self {
111 Self(value.into())
112 }
113
114 /// Returns the underlying string slice.
115 pub fn as_str(&self) -> &str {
116 &self.0
117 }
118 }
119
120 impl fmt::Display for $name {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 self.0.fmt(f)
123 }
124 }
125 };
126}
127
128capability_id!(
129 CapabilityName,
130 "Unique name for an [`Invocable`] capability."
131);
132capability_id!(ResourceId, "Unique identifier for a resource.");
133capability_id!(PromptId, "Unique identifier for a prompt template.");
134
135/// Describes an [`Invocable`] capability so it can be advertised to the model.
136///
137/// The spec is presented to the model alongside other available tools so that
138/// it can decide when to call the capability. The `input_schema` field should
139/// be a valid JSON Schema object describing the expected input shape.
140///
141/// # Example
142///
143/// ```rust
144/// use agentkit_capabilities::{CapabilityName, InvocableSpec};
145/// use serde_json::json;
146///
147/// let spec = InvocableSpec::new(
148/// CapabilityName::new("search"),
149/// "Search the codebase for a pattern",
150/// json!({
151/// "type": "object",
152/// "properties": {
153/// "query": { "type": "string" }
154/// },
155/// "required": ["query"]
156/// }),
157/// );
158///
159/// assert_eq!(spec.name.as_str(), "search");
160/// ```
161#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
162pub struct InvocableSpec {
163 /// The capability name that the model uses to reference this invocable.
164 pub name: CapabilityName,
165 /// A human-readable description shown to the model so it can decide when
166 /// to invoke this capability.
167 pub description: String,
168 /// A JSON Schema describing the expected shape of
169 /// [`InvocableRequest::input`].
170 pub input_schema: Value,
171 /// Arbitrary key-value metadata attached to the spec.
172 pub metadata: MetadataMap,
173}
174
175impl InvocableSpec {
176 /// Builds an invocable spec with empty metadata.
177 pub fn new(
178 name: impl Into<CapabilityName>,
179 description: impl Into<String>,
180 input_schema: Value,
181 ) -> Self {
182 Self {
183 name: name.into(),
184 description: description.into(),
185 input_schema,
186 metadata: MetadataMap::new(),
187 }
188 }
189
190 /// Replaces the spec metadata.
191 pub fn with_metadata(mut self, metadata: MetadataMap) -> Self {
192 self.metadata = metadata;
193 self
194 }
195}
196
197/// A request to execute an [`Invocable`] capability.
198///
199/// Created by the agentkit loop when the model emits a tool-call that targets
200/// a registered invocable. The `input` field contains the arguments the model
201/// provided, validated against the capability's [`InvocableSpec::input_schema`].
202#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
203pub struct InvocableRequest {
204 /// The JSON input arguments provided by the model.
205 pub input: Value,
206 /// The session in which this invocation occurs, if available.
207 pub session_id: Option<SessionId>,
208 /// The turn within the session, if available.
209 pub turn_id: Option<TurnId>,
210 /// Arbitrary key-value metadata attached to this request.
211 pub metadata: MetadataMap,
212}
213
214impl InvocableRequest {
215 /// Builds an invocable request with no session or turn ids.
216 pub fn new(input: Value) -> Self {
217 Self {
218 input,
219 session_id: None,
220 turn_id: None,
221 metadata: MetadataMap::new(),
222 }
223 }
224
225 /// Sets the session id.
226 pub fn with_session(mut self, session_id: impl Into<SessionId>) -> Self {
227 self.session_id = Some(session_id.into());
228 self
229 }
230
231 /// Sets the turn id.
232 pub fn with_turn(mut self, turn_id: impl Into<TurnId>) -> Self {
233 self.turn_id = Some(turn_id.into());
234 self
235 }
236
237 /// Replaces the request metadata.
238 pub fn with_metadata(mut self, metadata: MetadataMap) -> Self {
239 self.metadata = metadata;
240 self
241 }
242}
243
244/// The result of executing an [`Invocable`] capability.
245///
246/// Returned by [`Invocable::invoke`] on success. The `output` field carries
247/// the actual content while `metadata` can hold timing information, cache
248/// headers, or any other sideband data the caller might need.
249#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
250pub struct InvocableResult {
251 /// The content produced by the invocable.
252 pub output: InvocableOutput,
253 /// Arbitrary key-value metadata about the execution (e.g. latency, cache
254 /// status).
255 pub metadata: MetadataMap,
256}
257
258impl InvocableResult {
259 /// Builds an invocable result with empty metadata.
260 pub fn new(output: InvocableOutput) -> Self {
261 Self {
262 output,
263 metadata: MetadataMap::new(),
264 }
265 }
266
267 /// Builds a plain-text result.
268 pub fn text(text: impl Into<String>) -> Self {
269 Self::new(InvocableOutput::Text(text.into()))
270 }
271
272 /// Builds a structured result.
273 pub fn structured(value: Value) -> Self {
274 Self::new(InvocableOutput::Structured(value))
275 }
276
277 /// Builds an items result.
278 pub fn items(items: Vec<Item>) -> Self {
279 Self::new(InvocableOutput::Items(items))
280 }
281
282 /// Builds a data-reference result.
283 pub fn data(data: DataRef) -> Self {
284 Self::new(InvocableOutput::Data(data))
285 }
286
287 /// Replaces the result metadata.
288 pub fn with_metadata(mut self, metadata: MetadataMap) -> Self {
289 self.metadata = metadata;
290 self
291 }
292}
293
294/// The output payload of an [`Invocable`] execution.
295///
296/// Capabilities may return plain text, structured JSON, a sequence of
297/// conversation [`Item`]s, or a raw data reference depending on the nature of
298/// the work they perform.
299#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
300pub enum InvocableOutput {
301 /// A plain-text response.
302 Text(String),
303 /// A structured JSON value.
304 Structured(Value),
305 /// One or more conversation items (messages, tool results, etc.).
306 Items(Vec<Item>),
307 /// A reference to binary or text data (inline bytes, a URI, etc.).
308 Data(DataRef),
309}
310
311/// Describes a resource that a [`ResourceProvider`] can serve.
312///
313/// Resource descriptors are returned by
314/// [`ResourceProvider::list_resources`] so that the host or model can
315/// discover what data is available and request it by [`ResourceId`].
316#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
317pub struct ResourceDescriptor {
318 /// Unique identifier used to request this resource.
319 pub id: ResourceId,
320 /// A short, human-readable name for the resource.
321 pub name: String,
322 /// An optional longer description of the resource contents.
323 pub description: Option<String>,
324 /// The MIME type of the resource data (e.g. `"text/plain"`,
325 /// `"application/json"`).
326 pub mime_type: Option<String>,
327 /// Arbitrary key-value metadata attached to the descriptor.
328 pub metadata: MetadataMap,
329}
330
331impl ResourceDescriptor {
332 /// Builds a resource descriptor with no description or mime type.
333 pub fn new(id: impl Into<ResourceId>, name: impl Into<String>) -> Self {
334 Self {
335 id: id.into(),
336 name: name.into(),
337 description: None,
338 mime_type: None,
339 metadata: MetadataMap::new(),
340 }
341 }
342
343 /// Sets the description.
344 pub fn with_description(mut self, description: impl Into<String>) -> Self {
345 self.description = Some(description.into());
346 self
347 }
348
349 /// Sets the mime type.
350 pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
351 self.mime_type = Some(mime_type.into());
352 self
353 }
354
355 /// Replaces the descriptor metadata.
356 pub fn with_metadata(mut self, metadata: MetadataMap) -> Self {
357 self.metadata = metadata;
358 self
359 }
360}
361
362/// The payload returned when a resource is read.
363///
364/// Obtained by calling [`ResourceProvider::read_resource`] with a
365/// [`ResourceId`].
366#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
367pub struct ResourceContents {
368 /// The resource data, which may be inline text, inline bytes, a URI, or
369 /// an artifact handle.
370 pub data: DataRef,
371 /// Arbitrary key-value metadata about the read (e.g. ETag, last-modified).
372 pub metadata: MetadataMap,
373}
374
375impl ResourceContents {
376 /// Builds resource contents with empty metadata.
377 pub fn new(data: DataRef) -> Self {
378 Self {
379 data,
380 metadata: MetadataMap::new(),
381 }
382 }
383
384 /// Replaces the contents metadata.
385 pub fn with_metadata(mut self, metadata: MetadataMap) -> Self {
386 self.metadata = metadata;
387 self
388 }
389}
390
391/// Describes a prompt template that a [`PromptProvider`] can render.
392///
393/// Prompt descriptors are returned by [`PromptProvider::list_prompts`] so the
394/// host can discover available templates and present them to the user or model.
395#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
396pub struct PromptDescriptor {
397 /// Unique identifier used to request this prompt.
398 pub id: PromptId,
399 /// A short, human-readable name for the prompt.
400 pub name: String,
401 /// An optional longer description of when or why to use the prompt.
402 pub description: Option<String>,
403 /// A JSON Schema describing the arguments the prompt template accepts.
404 pub input_schema: Value,
405 /// Arbitrary key-value metadata attached to the descriptor.
406 pub metadata: MetadataMap,
407}
408
409impl PromptDescriptor {
410 /// Builds a prompt descriptor with no description and empty metadata.
411 pub fn new(id: impl Into<PromptId>, name: impl Into<String>, input_schema: Value) -> Self {
412 Self {
413 id: id.into(),
414 name: name.into(),
415 description: None,
416 input_schema,
417 metadata: MetadataMap::new(),
418 }
419 }
420
421 /// Sets the description.
422 pub fn with_description(mut self, description: impl Into<String>) -> Self {
423 self.description = Some(description.into());
424 self
425 }
426
427 /// Replaces the descriptor metadata.
428 pub fn with_metadata(mut self, metadata: MetadataMap) -> Self {
429 self.metadata = metadata;
430 self
431 }
432}
433
434/// The rendered output of a prompt template.
435///
436/// Returned by [`PromptProvider::get_prompt`] after applying the provided
437/// arguments to the template. The resulting items are typically prepended to
438/// the conversation transcript before the next model turn.
439#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
440pub struct PromptContents {
441 /// The conversation items produced by rendering the prompt.
442 pub items: Vec<Item>,
443 /// Arbitrary key-value metadata about the rendering.
444 pub metadata: MetadataMap,
445}
446
447impl PromptContents {
448 /// Builds prompt contents with empty metadata.
449 pub fn new(items: Vec<Item>) -> Self {
450 Self {
451 items,
452 metadata: MetadataMap::new(),
453 }
454 }
455
456 /// Replaces the contents metadata.
457 pub fn with_metadata(mut self, metadata: MetadataMap) -> Self {
458 self.metadata = metadata;
459 self
460 }
461}
462
463/// Shared execution context passed to all capability invocations.
464///
465/// The context carries the current session and turn identifiers so that
466/// capabilities can correlate their work with the broader conversation.
467/// A mutable reference is passed to every [`Invocable::invoke`],
468/// [`ResourceProvider::read_resource`], and [`PromptProvider::get_prompt`]
469/// call.
470///
471/// # Example
472///
473/// ```rust
474/// use agentkit_capabilities::CapabilityContext;
475/// use agentkit_core::{MetadataMap, SessionId, TurnId};
476///
477/// let session = SessionId::new("sess-1");
478/// let turn = TurnId::new("turn-1");
479/// let meta = MetadataMap::new();
480///
481/// let mut ctx = CapabilityContext {
482/// session_id: Some(&session),
483/// turn_id: Some(&turn),
484/// metadata: &meta,
485/// };
486///
487/// assert_eq!(ctx.session_id.unwrap().0, "sess-1");
488/// ```
489#[derive(Clone, Debug, PartialEq, Eq)]
490pub struct CapabilityContext<'a> {
491 /// The active session identifier, if one has been established.
492 pub session_id: Option<&'a SessionId>,
493 /// The current turn identifier within the session, if available.
494 pub turn_id: Option<&'a TurnId>,
495 /// Ambient metadata shared across all capabilities for this invocation.
496 pub metadata: &'a MetadataMap,
497}
498
499/// A capability that the model can invoke during a conversation turn.
500///
501/// Implement this trait to expose custom functionality -- database queries,
502/// API calls, file operations, code execution -- to the agentic loop. Each
503/// implementor provides a [`spec`](Invocable::spec) describing the capability
504/// and an async [`invoke`](Invocable::invoke) method that performs the work.
505///
506/// The agentkit loop discovers invocables through a [`CapabilityProvider`]
507/// and presents them to the model alongside regular tools.
508///
509/// # Example
510///
511/// ```rust
512/// use agentkit_capabilities::{
513/// CapabilityContext, CapabilityError, CapabilityName, Invocable,
514/// InvocableOutput, InvocableRequest, InvocableResult, InvocableSpec,
515/// };
516/// use async_trait::async_trait;
517/// use serde_json::json;
518///
519/// struct CurrentTime {
520/// spec: InvocableSpec,
521/// }
522///
523/// impl CurrentTime {
524/// fn new() -> Self {
525/// Self {
526/// spec: InvocableSpec::new(
527/// CapabilityName::new("current_time"),
528/// "Return the current UTC time",
529/// json!({ "type": "object" }),
530/// ),
531/// }
532/// }
533/// }
534///
535/// #[async_trait]
536/// impl Invocable for CurrentTime {
537/// fn spec(&self) -> &InvocableSpec {
538/// &self.spec
539/// }
540///
541/// async fn invoke(
542/// &self,
543/// _request: InvocableRequest,
544/// _ctx: &mut CapabilityContext<'_>,
545/// ) -> Result<InvocableResult, CapabilityError> {
546/// Ok(InvocableResult::text("2026-03-22T12:00:00Z"))
547/// }
548/// }
549/// ```
550#[async_trait]
551pub trait Invocable: Send + Sync {
552 /// Returns the specification that describes this capability to the model.
553 fn spec(&self) -> &InvocableSpec;
554
555 /// Executes the capability with the given request and context.
556 ///
557 /// # Arguments
558 ///
559 /// * `request` - The invocation request containing the model-provided input
560 /// and session identifiers.
561 /// * `ctx` - The shared capability context for this turn.
562 ///
563 /// # Errors
564 ///
565 /// Returns [`CapabilityError`] if the capability is unavailable, the input
566 /// is invalid, or execution fails.
567 async fn invoke(
568 &self,
569 request: InvocableRequest,
570 ctx: &mut CapabilityContext<'_>,
571 ) -> Result<InvocableResult, CapabilityError>;
572}
573
574/// A provider of named data resources that can be listed and read.
575///
576/// Implement this trait to expose data sources -- files on disk, database
577/// rows, API responses -- to the model. The agentkit MCP bridge uses this
578/// trait to surface MCP-server resources into the agentic loop.
579///
580/// # Example
581///
582/// ```rust
583/// use agentkit_capabilities::{
584/// CapabilityContext, CapabilityError, ResourceContents,
585/// ResourceDescriptor, ResourceId, ResourceProvider,
586/// };
587/// use agentkit_core::{DataRef, MetadataMap};
588/// use async_trait::async_trait;
589///
590/// struct StaticFile;
591///
592/// #[async_trait]
593/// impl ResourceProvider for StaticFile {
594/// async fn list_resources(&self) -> Result<Vec<ResourceDescriptor>, CapabilityError> {
595/// Ok(vec![ResourceDescriptor {
596/// id: ResourceId::new("readme"),
597/// name: "README.md".into(),
598/// description: Some("Project readme".into()),
599/// mime_type: Some("text/markdown".into()),
600/// metadata: MetadataMap::new(),
601/// }])
602/// }
603///
604/// async fn read_resource(
605/// &self,
606/// id: &ResourceId,
607/// _ctx: &mut CapabilityContext<'_>,
608/// ) -> Result<ResourceContents, CapabilityError> {
609/// if id.as_str() == "readme" {
610/// Ok(ResourceContents {
611/// data: DataRef::InlineText("# Hello".into()),
612/// metadata: MetadataMap::new(),
613/// })
614/// } else {
615/// Err(CapabilityError::Unavailable(format!(
616/// "unknown resource: {id}"
617/// )))
618/// }
619/// }
620/// }
621/// ```
622#[async_trait]
623pub trait ResourceProvider: Send + Sync {
624 /// Lists all resources currently available from this provider.
625 ///
626 /// # Errors
627 ///
628 /// Returns [`CapabilityError`] if the provider cannot enumerate its
629 /// resources (e.g. a network timeout).
630 async fn list_resources(&self) -> Result<Vec<ResourceDescriptor>, CapabilityError>;
631
632 /// Reads the contents of the resource identified by `id`.
633 ///
634 /// # Arguments
635 ///
636 /// * `id` - The unique resource identifier, as returned in a
637 /// [`ResourceDescriptor`].
638 /// * `ctx` - The shared capability context for this turn.
639 ///
640 /// # Errors
641 ///
642 /// Returns [`CapabilityError::Unavailable`] if the resource does not exist
643 /// or [`CapabilityError::ExecutionFailed`] if reading fails.
644 async fn read_resource(
645 &self,
646 id: &ResourceId,
647 ctx: &mut CapabilityContext<'_>,
648 ) -> Result<ResourceContents, CapabilityError>;
649}
650
651/// A provider of parameterised prompt templates.
652///
653/// Implement this trait to offer reusable prompt templates that the host can
654/// render with user-supplied arguments and inject into the conversation
655/// transcript. The agentkit MCP bridge uses this trait to surface MCP-server
656/// prompts into the agentic loop.
657#[async_trait]
658pub trait PromptProvider: Send + Sync {
659 /// Lists all prompt templates currently available from this provider.
660 ///
661 /// # Errors
662 ///
663 /// Returns [`CapabilityError`] if the provider cannot enumerate its
664 /// prompts.
665 async fn list_prompts(&self) -> Result<Vec<PromptDescriptor>, CapabilityError>;
666
667 /// Renders a prompt template with the given arguments.
668 ///
669 /// # Arguments
670 ///
671 /// * `id` - The unique prompt identifier, as returned in a
672 /// [`PromptDescriptor`].
673 /// * `args` - A JSON value containing the template arguments, validated
674 /// against the prompt's [`PromptDescriptor::input_schema`].
675 /// * `ctx` - The shared capability context for this turn.
676 ///
677 /// # Errors
678 ///
679 /// Returns [`CapabilityError::Unavailable`] if the prompt does not exist,
680 /// [`CapabilityError::InvalidInput`] if the arguments are malformed, or
681 /// [`CapabilityError::ExecutionFailed`] if rendering fails.
682 async fn get_prompt(
683 &self,
684 id: &PromptId,
685 args: Value,
686 ctx: &mut CapabilityContext<'_>,
687 ) -> Result<PromptContents, CapabilityError>;
688}
689
690/// A bundle of capabilities from a single source.
691///
692/// [`CapabilityProvider`] groups [`Invocable`]s, [`ResourceProvider`]s, and
693/// [`PromptProvider`]s that originate from the same backend -- for example,
694/// a single MCP server or a built-in tool collection. The agentkit loop
695/// collects providers and merges their contents into the unified tool list
696/// presented to the model.
697///
698/// # Example
699///
700/// ```rust
701/// use std::sync::Arc;
702/// use agentkit_capabilities::{
703/// CapabilityProvider, Invocable, PromptProvider, ResourceProvider,
704/// };
705///
706/// struct EmptyProvider;
707///
708/// impl CapabilityProvider for EmptyProvider {
709/// fn invocables(&self) -> Vec<Arc<dyn Invocable>> {
710/// vec![]
711/// }
712///
713/// fn resources(&self) -> Vec<Arc<dyn ResourceProvider>> {
714/// vec![]
715/// }
716///
717/// fn prompts(&self) -> Vec<Arc<dyn PromptProvider>> {
718/// vec![]
719/// }
720/// }
721/// ```
722pub trait CapabilityProvider: Send + Sync {
723 /// Returns all invocable capabilities offered by this provider.
724 fn invocables(&self) -> Vec<Arc<dyn Invocable>>;
725
726 /// Returns all resource providers offered by this provider.
727 fn resources(&self) -> Vec<Arc<dyn ResourceProvider>>;
728
729 /// Returns all prompt providers offered by this provider.
730 fn prompts(&self) -> Vec<Arc<dyn PromptProvider>>;
731}
732
733/// Errors that can occur when interacting with capabilities.
734///
735/// This enum is used as the error type across all capability traits
736/// ([`Invocable`], [`ResourceProvider`], [`PromptProvider`]).
737#[derive(Debug, Error)]
738pub enum CapabilityError {
739 /// The requested capability, resource, or prompt is not available.
740 ///
741 /// Returned when the identifier does not match any registered item or
742 /// when the provider is temporarily offline.
743 #[error("capability unavailable: {0}")]
744 Unavailable(String),
745
746 /// The input provided to the capability is invalid.
747 ///
748 /// Returned when the model-supplied arguments fail schema validation or
749 /// contain values outside the expected domain.
750 #[error("invalid capability input: {0}")]
751 InvalidInput(String),
752
753 /// The capability encountered an error during execution.
754 ///
755 /// Returned for runtime failures such as network timeouts, I/O errors,
756 /// or unexpected backend responses.
757 #[error("capability execution failed: {0}")]
758 ExecutionFailed(String),
759}