aster/aster_apps/
resource.rs1use serde::{Deserialize, Serialize};
2use utoipa::ToSchema;
3
4#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
7#[serde(rename_all = "camelCase")]
8pub struct CspMetadata {
9 #[serde(skip_serializing_if = "Option::is_none")]
11 pub connect_domains: Option<Vec<String>>,
12 #[serde(skip_serializing_if = "Option::is_none")]
14 pub resource_domains: Option<Vec<String>>,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
19#[serde(rename_all = "camelCase")]
20pub struct UiMetadata {
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub csp: Option<CspMetadata>,
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub domain: Option<String>,
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub prefers_border: Option<bool>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
34#[serde(rename_all = "camelCase")]
35pub struct ResourceMetadata {
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub ui: Option<UiMetadata>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
44#[serde(rename_all = "camelCase")]
45pub struct McpAppResource {
46 pub uri: String,
48 pub name: String,
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub description: Option<String>,
53 pub mime_type: String,
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub text: Option<String>,
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub blob: Option<String>,
61 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
63 pub meta: Option<ResourceMetadata>,
64}
65
66impl McpAppResource {
67 pub fn new_html(uri: String, name: String, html: String) -> Self {
68 Self {
69 uri,
70 name,
71 description: None,
72 mime_type: "text/html;profile=mcp-app".to_string(),
73 text: Some(html),
74 blob: None,
75 meta: None,
76 }
77 }
78
79 pub fn new_html_with_csp(uri: String, name: String, html: String, csp: CspMetadata) -> Self {
80 Self {
81 uri,
82 name,
83 description: None,
84 mime_type: "text/html;profile=mcp-app".to_string(),
85 text: Some(html),
86 blob: None,
87 meta: Some(ResourceMetadata {
88 ui: Some(UiMetadata {
89 csp: Some(csp),
90 domain: None,
91 prefers_border: None,
92 }),
93 }),
94 }
95 }
96
97 pub fn with_description(mut self, description: String) -> Self {
98 self.description = Some(description);
99 self
100 }
101
102 pub fn with_ui_metadata(mut self, ui_metadata: UiMetadata) -> Self {
103 if let Some(meta) = &mut self.meta {
104 meta.ui = Some(ui_metadata);
105 } else {
106 self.meta = Some(ResourceMetadata {
107 ui: Some(ui_metadata),
108 });
109 }
110 self
111 }
112}